Unlocking Microsoft C# v2.0 Programming Secrets

Next Section
by Richard P. Braden
Wordware Publishing © 2006 (298 pages)
ISBN:9781556220975

From discussions of the IDE and the basics of code placement in C# to the creation of message boxes and menus, this straightforward guide details how to insert common algorithmic functions in C# into one’s source code.

Table of Contents
Unlocking Microsoft C# v2.0 Programming Secrets
Introduction
Chapter 1 | - | A Quick Dash through the Second Half of the Twentieth Century
Chapter 2 | - | Personal Communication with a Digital Computer
Chapter 3 | - | The Microsoft Integrated Development Environment (IDE)
Chapter 4 | - | Cloning an Existing Project
Chapter 5 | - | Passing Information between Windows
Chapter 6 | - | Visual Studio C# Code Placement
Chapter 7 | - | Classes, Constructors, Overloading, and Help
Chapter 8 | - | Data Types and Declarations
Chapter 9 | - | Conversions between Data Types
Chapter 10 | - | String Manipulation and Online Data Storage (Arrays)
Chapter 11 | - | Branching and Looping
Chapter 12 | - | Arithmetic
Chapter 13 | - | Message Boxes
Chapter 14 | - | Displaying Data (Lists) on a Window
Chapter 15 | - | Plotting Data on a Window
Chapter 16 | - | Printing Text
Chapter 17 | - | Creating, Reading, and Writing Files
Chapter 18 | - | Menus (Main and Pop-up)
Chapter 19 | - | Standard Controls
Chapter 20 | - | Logic Statements, Enabling and Disabling Controls, and Focus
Chapter 21 | - | Timers and Clocks
Chapter 22 | - | Utilizing IDE Visual Studio C# Prebuilt Code
Chapter 23 | - | Creating a Top-Level Program for Groups of Executables
Chapter 24 | - | Creating a Project Icon
Chapter 25 | - | Creating an Install Program
Chapter 26 | - | Methods (Subroutines)
Chapter 27 | - | Game-Making and Animation
Chapter 28 | - | Picture Boxes
Chapter 29 | - | “Lost ” Controls/Graphics
Appendix A | - | A Description of Keeping Track
Appendix B | - | C++ to C# Cross Reference
Next Section


Previous Section Next Section
by Richard P. Braden
Wordware Publishing © 2006 (298 pages)
ISBN:9781556220975

From discussions of the IDE and the basics of code placement in C# to the creation of message boxes and menus, this straightforward guide details how to insert common algorithmic functions in C# into one’s source code.

Back Cover

Unlocking Microsoft C# v2.0 Programming Secrets provides a guide for working with the Visual C# compiler. From discussions of the IDE and the basics of code placement in C# to the creation of message boxes and menus, this book details how to insert common algorithmic functions in C# into one’s source code.

Learn how to:

  • Pass information from one window to another.
  • Use classes, constructors, and overloading in Visual C# code.
  • Convert between data types.
  • Display and plot data.
  • Create, read, and write files.
  • Use logic statements for decision making.
  • Enable and disable controls.
  • Build a top-level program and an install program for groups of executables.
  • Create a project icon.

About the Author

Richard Braden, Ph.D., has over 30 years of experience in advanced programming with a host of different platforms and languages. He was an instructor in the mathematics and aeronautics departments at the United States Air Force Academy, served as an officer in the U.S. Air Force, and had a successful civilian career at Martin-Marietta. He has also taught programming at various universities and community colleges across the country.

Previous Section Next Section


Richard P. Braden, Ph.D.
Wordware Publishing, Inc.

Library of Congress Cataloging-in-Publication Data

Braden, Richard P.

Unlocking Microsoft C# v2.0 programming secrets / by Richard P. Braden.

p. cm.

Includes index.

ISBN-13: 978-1-55622-097-5

ISBN-10: 1-55622-097-9 (pbk.)

1. C# (Computer program language) 2. Compilers (Computer programs) I. Title.

QA76.65B73 2005

005.13’3--dc22 2004030069

CIP

© 2006, Wordware Publishing, Inc.

All Rights Reserved

2320 Los Rios Boulevard
Plano, Texas 75074

No part of this book may be reproduced in any form or by any means without permission in writing from Wordware Publishing, Inc.

ISBN-13: 9781556220975

ISBN-10: 1-55622-097-9

10 9 8 7 6 5 4 3 2 1

0508

Microsoft and Visual C# are registered trademarks of Microsoft Corporation in the United States and/or other countries.

Other brand names and product names mentioned in this book are trademarks or service marks of their respective companies. Any omission or misuse (of any kind) of service marks or trademarks should not be regarded as intent to infringe on the property of others. The publisher recognizes and respects all marks used by companies, manufacturers, and developers as a means to distinguish their products.

This book is sold as is, without warranty of any kind, either express or implied, respecting the contents of this book and any disks or programs that may accompany it, including but not limited to implied warranties for the book’s quality, performance, merchantability, or fitness for any particular purpose. Neither Wordware Publishing, Inc. nor its dealers or distributors shall be liable to the purchaser or any other person or entity with respect to any liability, loss, or damage caused or alleged to have been caused directly or indirectly by this book.

All inquiries for volume purchases of this book should be addressed to Wordware Publishing, Inc., at the above address. Telephone inquiries may be made by calling:

(972) 423-0090

Acknowledgments

My thanks to the members of the Microsoft team who have created and distributed the C# 2.0 compiler. The information in this text came from them. They include Dan Fernandez, Raj Pai, Seth Adams, Chris Jolley, Michael Blome, and Rusty Miller on the West Coast, and especially Phil Suckiel on the East Coast (a man who demonstrates an in-depth knowledge of the C# language).

My thanks also to Beth Kohler, senior editor at Wordware, for putting the text together in a coherent manner. Some of my neighbors took portions of the 61 projects shown in this text and offered to load and run them on their PCs to guarantee that the companion files were created properly. I am forever in their debt.


Introduction

Microsoft Corporation’s newest compiler, C# (pronounced “C sharp”), is now with us, and may become one of the most celebrated compilers of the 21st century — if we can learn how to use its immense capabilities. Some have questioned the immediate value of this new compiler, since it was only a decade ago that C++ emerged from the computational think tanks of the world. “What’s wrong with continuing to use C++ ?” they ask.

The answer is this: C++ will continue to serve some of us comfortably in the near term, but there are features built into C# that make life easier when we program for the Internet and other remote processes. We must become more attuned to the needs of the Internet.

In some cases, however, C# adds a degree of complexity to the compiler that was not there before. For example, Charles Petzold mentions in his book Programming Microsoft Windows with C# that there are half a dozen forms of the StreamWriter constructor that use file names, three forms that use a Stream object, and several other forms for special uses. Some of the common C# commands have as many as 16 variations. Petzold also discusses encoding schemes like Unicode, UTF7, UTF8, and finally, ASCII (thank goodness). A bead of perspiration broke out on my forehead when I read that. How could I possibly remember all the variations of the “simple” C commands that I have learned to love over the years?

Then I remembered how it was 15 years ago when the C language first evolved into the C++ language and program architecture became object-oriented. C++ was not a superfluous change to a basic C script — the change forced us to become more focused programmers since all the C++ procedures were narrowly defined and all the variables were hard-typed so we could not misuse them like we did in the old C days. The new methods of creating source code were quite a change for the, shall we say, “experienced programmer.” There were so many new ways to make mistakes in the code we were attempting to write. In retrospect, however, object-oriented programming was a godsend.

Along with the increased complexity of the compilers, more attention was given by the compiler manufacturers to helps that could lead the average programmer out of the quandary of overspecific compiler statements. You will be pleased to find that the new generation of helps in the Microsoft C# language outstrips any automated technical support you have encountered so far.

These helps are not just laying dormant in a multi-meg text document that accompanies the new C# compiler — they are active bells, whistles, and gongs that warn the programmer when he has done something wrong in preparing source code or while combining multiple groups of source code into one integrated executable (an msi). It’s like having your favorite computer science professor looking over your shoulder every time you write source code — in real time no less! The new compilers have protected themselves from an onslaught of poor overall planning on the part of the programmer, poor sequencing of computer tasks, and poor statement choices (like the dredging up of old statements learned 20 years ago that are no longer in the repertoire). If a person wants to learn to program in C#, then she had better learn the nuances of the C# language!

This is not to say that the nuances are that great. If you have the mindset of a computer programmer, then the C# nuances will be embedded in your brain in a few weeks, and the lingering memories of the old ways of creating computer task sequences with C or C++ will fade into the sunset.

Like every new language, however, the C# architects included multiple ways of accomplishing the same task within the C# language. This leads to confusion among the newly washed members of the programming community. For example, why is there more than one way in C# to open a file and read it? And the answer is: Because the old methods that have appeared in FORTRAN, COBOL, Pascal, ADA, Smalltalk, Java, and so forth since the 1960s all have their devotees in the programming community, and no compiler manufacturer wants to alienate them. Also, within a given compiler-creating architectural group there are wide differences of opinion on how basic computing tasks should be bundled and provided to the programmer — hence, the redundancy continues.

Should the writers of a computer science handbook attempt to show all the methods of performing a standard programming task to programmers who are new to C#? One can certainly do this, of course, but the ensuing work may be three inches thick and printed in eight-point text. To avoid this lapse into redundancy and make the transition to C# easier, we decided on one way to perform each common software programming task and cast aside all others. We picked the best way to accomplish all the common programming tasks, and those best ways are in this book.

In some cases, code snippets were not forceful enough (or self-explanatory enough) to demonstrate how C# statements should be used in programs, so we undertook the task of creating a set of programs under the main title Keeping Track, a set of programs to oversee one’s stocks/bonds/funds in the financial market. Keeping Track (KT) is a collection of seven C# programs bundled under one entry point. The KT suite of programs is described more fully in Appendix A. The programs are yours to experiment with — the companion files (available at www.wordware.com/unlockingcsharp) contain both the source code (8,300 lines) and the compiled code for KT. With some thought and experimentation you can elevate KT from a simple set of humdrum programs to a “super set.” Have at it!

As we wrote this book it became harder and harder to point the reader to particular snippets of code without hand-tagging every line, so we finally placed the demonstration source code and all the KT source code into Microsoft Word and gave each line a separate index number on the left edge of the page, like “EP0039.” With the help of a word processor like Word or Notepad and its search capability, the reader can locate all the referenced code in a flash. The code can be extracted and moved to new projects using standard copy and paste techniques.

One element of stress still remains: How can the reader look up C# code in an indexed listing when he doesn’t know the name of the statement needed? (Remember how it was when you asked someone how to spell a word, and that person told you to look it up in the dictionary? How can you look up a word in the dictionary when you don’t know how to spell it?) For example, if you have performed C programming in the past, then you can think of two commands to copy a file: CopyFile and FileCopy. Neither one of these commands is found in C#, which uses FileInfo followed by CopyTo. To help you resolve this dilemma, Appendix B provides a cross-reference to most of the common C and C++ commands and their new C# counterparts.

System Architectural Differences between C++ and C#

There are some nuances about the C# compiler (and more correctly, the .NET Framework in which the C# compiler plays a significant role) that should be mentioned from the outset. There are some features of the .NET Framework that will excite you and others that will disappoint you. Part of the disappointment is due to the fact that the Microsoft compiler group is anxious to move the world’s computer programmers out of a climate where a given compiler produces machine code that executes properly in a narrowly defined set of computing machines, like PCs for example, but fails in other applications. They seek to create source code in an intermediate language that will service all known computing hardware formats.

To accomplish this universal source coding technique, Microsoft did exactly what the Java group (Sun Microsystems) did several years ago. Microsoft wrote an outstanding C# compiler that produces programs written in an intermediate language (not machine code, as we have learned to expect).

How did Java pull off this radical departure from expected programming etiquette? They wrote “cookies” (also known as run-time language compilers) that were placed on each PC or Macintosh or Sun machine or IBM mainframe or European computer that took the intermediate code and converted it to machine code that the individual computing machine could understand. It sounds a little goofy, but it worked!

So we warn you in advance that all is not as it may seem in this Microsoft C# compiler! The source code that you generate will compile in the Microsoft-provided compiler, and each Microsoft program will produce an executable that is intermediate code and not machine code. You can execute each program with the usual PC methods. But what you don’t know is this: A common run-time language is present on your PC as a giant cookie, and if you do not have that common run-time cookie on your PC then you cannot run the programs!

What all this means is this: If you write a program and give it to a friend across the street for execution on his PC, there is a good chance that the program will fail at run time. But all is not lost! The person to whom you gave the program can load this giant cookie on his PC by doing one of two things:

  • Get a copy of the Microsoft Prerequisites CD that is shipped with every copy of the C# compiler and place it in the CD reader. Use Explorer to open the .NET Framework subdirectory and run dotnetfx.exe (it’s the only file in the subdirectory). The self-extracting process begins to place the 23 MB cookie at several locations in the Windows subdirectory. The extraction process takes about five minutes, and now the program is ready to be executed. You, of course, already have the .NET Framework cookie on your PC because it comes with the C# compiler.

  • A link to the latest version of dotnetfx.exe is available on the download page for this book. Navigate to www.wordware.com/unlockingcsharp and click the dotnetfx.exe link. When you get to the Microsoft page, click the Download button and follow the instructions.

Does this process seem a little obtuse to you? A 23 MB cookie? Well, it is. But Microsoft is moving quickly to place this giant cookie in every copy of every operating system they manufacture by the end of 2005. And, since most owners of Windows XP have agreed to allow continuous automatic updates of their operating system via the Internet, most of the Microsoft operating systems will include the results of dotnetfx.exe in the near future. Meanwhile, in the near future, you and your neighbor across the street are going to have to work something out so you can trade code easily.

By early 2006 every Microsoft operating system should have this problem resolved.


Visual Studio C# Version 2.0

Every statement in this book applies to both the Professional and Home versions of C# 2.0 and the abbreviated compiler, Visual C# 2005 Express Edition. We hope you enjoy this book and the C# language. Welcome to computing in the 21st century!


Content Summary

The early chapters of this book were written for those who are new to programming, but they are enjoyable reading for all. A summary of the chapters follows:

Chapter 1 — A Quick Dash through the Second Half of the Twentieth Century. How we got where we are today in digital computing.

Chapter 2 — Personal Communication with a Digital Computer. The evolution of the keyboard and the mouse.

Chapter 3 — The Microsoft Integrated Development Environment (IDE). The Microsoft IDE is couched within the Microsoft Visual Studio suite, where programmers install compilers to create computer programs in multiple languages, like Basic, the variants of C, and Java. This chapter emphasizes the minimum number of features in the Visual Studio Suite IDE that a programmer must master to build acceptable programs. It includes an IDE walk-through and directions on how to create your first program.

Chapter 4 — Cloning an Existing Project. Once you create a project in the IDE, you cannot move the project folder elsewhere on your hard drive and continue to communicate between the IDE and that project (because the pathing information is now befuddled). We show you how to clone a project, move it elsewhere, and retrofit it to do further work.

Chapter 5 — Passing Information between Windows. In many of today’s C class compilers the architects made it easy for programmers to pass objects between windows by including the *.h declarations files. Not so with Visual Studio C#! The programmer must first create a handle to the main window that owns the necessary object, pass that handle to each new child window that is created, and allow the child windows the proper access to that object through the main window handle. Also, the “in-direction” symbol (->) is not used in Visual Studio C#.

Chapter 6 — Visual Studio C# Code Placement. This chapter discusses features of C# that differ from C++, along with new features. It is aimed primarily at the new programmer, and includes a tutorial called CodePlacement. We suggest that the CodePlacement project be placed in the Visual Studio Microsoft IDE main window and exercised as you read this chapter.

Chapter 7 — Classes, Constructors, Overloading, and Help. This is a short discussion of the Visual Studio C# classes, object-oriented programming, overloading of C# functions, and the help tools available within the IDE.

Chapter 8 — Data Types and Declarations. The common Visual Studio C# data types, Hungarian notation, where to place declarations in the source code, variable declarations, and enumerations.

Chapter 9 — Conversions between Data Types. The specific statements that convert a given data type to any other data type. Also, ANSIString operators are listed.

Chapter 10 — String Manipulation and Online Data Storage. Manipulating the characters within a string, and the declaration, use, and operation of arrays.

Chapter 11 — Branching and Looping. if, for, foreach, while, do…while, break, goto, and switch…case.

Chapter 12 — Arithmetic. Addition, subtraction, multiplication, division, exponentiation, logarithmic, and arithmetic parentheses.

The remaining chapters discuss specific programming topics:

Chapter 13 — Message Boxes. Both the simple message box that is used for information only and the more complex message box that requires a reply by the user are discussed. This chapter includes an example of a general-purpose message box that can be used whenever the standard message box construct does not meet the programmer’s needs.

Chapter 14 — Displaying Data (Lists) on a Window. This is a demonstration of how to set up a window (form) to display text.

Chapter 15 — Plotting Data on a Window. The art of creating a primitive but respectable-looking X-Y plot on a window is demonstrated.

Chapter 16 — Printing Text. Printing text onto a sheet of paper.

Chapter 17 — Creating, Reading, and Writing Files. One way to create, read, and write to a file.

Chapter 18 — Menus (Main and Pop-up). The IDE, in conjunction with the Visual Studio C# compiler and attached IDE tools, presents the simplest methods for preparing both main and pop-up menus that you have ever experienced. Both main and pop-up builds are demonstrated.

Chapter 19 — Standard Controls. The standard controls that a programmer may place onto a window have not changed with the advent of C#. A new item called a Tab control is now available and easy to construct within the IDE. This chapter summarizes all the controls available for Windows programming to date.

Chapter 20 — Logical Statements, Enabling and Disabling Controls, and Focus. There are few surprises in this chapter. Logic statements have not changed with the advent of C#. Enabling and disabling controls have changed only in the explicit construct of the enable-disable statement. Placing the focus onto a particular control on a window has been relaxed somewhat by the C# compiler, and this produces both good and bad situations. An example is shown where improper focus causes problems in a project.

Chapter 21 — Timers and Clocks. The constructs for both timers and clocks in Visual Studio C# are much simpler than their counterparts in C and C++. Both constructs are demonstrated.

Chapter 22 — Utilizing IDE Visual Studio C# Prebuilt Code. This is a cursory look into the common dialogs found in C#.

Chapter 23 — Creating a Top-Level Program for Groups of Executables. This topic was included at the request of programmers who are responsible for writing the entry point for a group of Windows programs. The concept is simple and the code is simple — simple and repetitive.

Chapter 24 — Creating a Project Icon. This is a not-so-simple task if you have never done it before. The code to place a project icon on the desktop is now considered a deployment task and is discussed in the next chapter.

Chapter 25 — Creating an Install Program. If you ever need to create a stand-alone set of programs that form a new application for PCs and so forth, then you will need the information found in this chapter. The formal deployment process as created by Microsoft is discussed.

Chapter 26 — Methods (Subroutines). Every compiler has its rules about including freestanding subroutines (now called methods or procedures) in the collection of source code statements. This chapter shows how to create a simple freestander that is available to all code that supports all the windows in a project.

Chapter 27 — Game-Making and Animation. Demonstrations of simple game-making and animation.

Chapter 28 — Picture Boxes. A short discussion of the PictureBox control in the IDE toolbox.

Chapter 29 — “Lost” Controls/Graphics. Performing damage control on projects that lose controls or graphics.

Appendix A — A Description of Keeping Track.

Appendix B — C++ to C# Cross Reference.


Companion Files

The companion files included with this book contain a variety of files and directories.

To install these items on your PC hard drive, navigate to www.wordware.com/unlockingcsharp. There are two links below Step 2: Program Files and Unlocking C#. Click on each one and save the files to a location on your computer where you can find them for the next step. (Be sure you have downloaded the dotnetfx.exe software.)

Find each of the files (ProgramFiles.zip and UnlockingCSharp.zip) and unzip them. Then double-click on each unzipped file (ProgramFiles.msi and UnlockingCSharp.msi) to transfer these files and directories to your hard drive.

The wizard that appears when UnlockingCSharp.msi is chosen offers you the chance to change the absolute path to the directory where these files will be located: Visual Studio 2005. This is the directory in which the Microsoft Visual Studio 2005 suite chooses to place your projects. The default path to this directory is C:\Documents and Settings\Owner\ My Documents\Visual Studio 2005.

However, if you are sharing your PC with someone else (and the operating system is XP), you may have elected to share the machine by replacing the Owner directory above with the names of the people doing the sharing. For example, if your name is “Harvey Wallbanger,” you may have set the path to Visual Studio 2005 in your personal directories as C:\Documents and Settings\Harvey Wallbanger\My Documents\Visual Studio 2005.

The default path shown above works for PCs that do not share; if you share you may have to change the absolute path to Visual Studio 2005 slightly.

The files in ProgramFiles.msi are always routed to C:\Program Files, so there is no need to change this path.

These are the files/directories in UnlockingCSharp.msi (C:\Documents and Settings\Owner\My Documents\Visual Studio 2005\):

Projects:

  • DemosSourceCode (26 projects)

  • KT Source Code (9 projects)

  • GS Source Code (8 projects)

  • TI Source Code (8 projects, with no data)

  • KT101 (9 projects)

  • Financial Indexes (2 projects)

  • Payup (1 project)

Tagged KT-Demo Code:

  • App C -- KTVC#TaggedCode.doc. This is a Word file containing all the code in the KT (Keeping Track) project.

  • App D -- DemoVC#TaggedList.doc. This is a Word file containing all of the demonstration code in the book.

  • GS Install.doc. This contains the tagged code for GS.

User Docs:

  • CalendarConversion.doc

  • FinancialIndexesManual.doc

  • KTForms.doc

  • KTInstallation.doc

  • KTUserManualV4.doc

These are the files/directories in ProgramFiles.msi:

  • KT (C:\Program Files\KT) — data files to support KT. These files are copied to your PC hard drive to exercise Chapter 23 on your machine.

  • GS (C:\Program Files\GS) — data files to support GS.

  • FIFT (C:\Program Files\FIFT) — data files to support Financial

    Indexes.

  • TI (C:\Program Files\TI) — data files to support TI.


Chapter 1: A Quick Dash through the Second Half of the Twentieth Century

By the early 1960s several research laboratories and electronic firms around the world had demonstrated the ability to fabricate high-speed digital central processing units (CPUs) that could step through a sequential set of commands and do errorless arithmetic. A lot has changed since that time, but digital computers continue to step through sequential series of commands and produce arithmetic, linguistic, and visual results that are reported to the world as hard copy (printed pages), data tapes, screen presentations, files, speaker outputs, music, light shows, CDs, etc.

The early computers were surrounded by an elite group of scientists and engineers who learned to create “machine code,” the sequential set of commands that a CPU deciphers to produce outputs. Each “word” of machine code started out at 8 bytes per word, 8 bits per bite (a “bit” is a 0 or a 1 for visual reference, but on the hardware it is most commonly represented bya0DCvoltage or a +4 DC voltage). So the first machine code “word” consisted of 64 bits and each bit had two states. The 8-byte word was quickly replaced by the 16-byte word, and 32 bytes followed in the late 1980s. Today the machine code 32-byte word is the most common, but the 64-byte word exists in some computers (especially number-crunching computers, like the Cray scientific computer). Some of the most sophisticated 64-byte CPUs that were crafted in the 1990s were placed in game platforms like the PlayStation 2. The larger the machine code word size, the easier it is for programmers to program the machine (because one 64-byte word can command so many variations within the CPU).

Those early-generation scientists and engineers knew that a more sophisticated means had to be devised for creating machine code because it took weeks and months for such a group to create machine code that the CPU could process in under one minute. With their primitive machine code generation techniques there wouldn’t be enough people in the entire world to service these digital machines. Everyone would have to become a programmer.

Computer hardware companies sprang up everywhere, with most of their employees focused on producing faster and more reliable CPUs and ancillary machinery (later known as peripherals) that could feed input data to the CPU in a predictable fashion. A much smaller group investigated better techniques for communicating with their CPUs (the human-to-machine interface), and they became the software gurus.

IBM (International Business Machines) quickly became a leader in this new industry, and thanks to some innovative scientists at the Massachusetts Institute of Technology (MIT) they became proficient at building storage devices for data. (Remember the pictures of a tiny glass-composition bead suspended in a matrix of wires about the thickness of a human hair? That was the first data storage device. Each glass bead stored one bit of information.) These storage devices soon became known as computer “memory.” Next, IBM constructed a mini-software device known as an assembler that allowed people besides the elite to communicate with a digital computer. Simple assembly statements were converted to machine code using a converter. This was a worthy initial effort at easing computer programming woes.

Soon every manufacturer of computer machinery advertised its own operating system and its own assembler. A person who wanted to specialize in software to command a specific brand of computer had to immerse himself in the details of the operating system and its assembly language.

The notion of a computer language that was not fixed at the machine code level (an intermediate language, one that received inputs from human beings and produced machine code for several types of digital computers) piqued the interest of scientists all over the world. In the United States it was left to IBM to announce the first general-purpose intermediate language system, a system named Formula Translation, which was shortened to FORTRAN. The huge code package that transformed programmers’ entries into IBM machine code was known as a compiler. The compiler read each line of instruction that was created by a programmer on an 80-column punched card. At this time the entry point to the computer was the card reader, a mechanical device that regularly ripped and tore at one’s thick packet of punched cards and created bedlam at the computer center. Each punched card contained one FORTRAN statement. A typical civil engineering aircraft structures program created at a university was 2000 to 3000 cards long, and the owner of the card deck always kept his fingers crossed that the reader would not fault during the reading, which took 2 to 3 minutes to complete. At the first faulty read the device stopped its processes and several red lights began blinking. It was time to start the read process all over again.

This collection of punched cards became known as the source code — another term that has survived from that era. The programmer was the source of the source code, and his only means of creating source code was the punched-card machine built by IBM.

In England and Europe a consortium of scientists, engineers, and professors who cared about intermediate languages gathered their thoughts around a language named Pascal (named after Blaise Pascal, a 17th-century French mathematician who proposed the first adding machine). When the European and North American groups combined their interests they formed the IEEE (Institute of Electrical and Electronics Engineers), which laid the groundwork for committees of interested persons to prepare standards that these intermediate languages should follow in the machine code creation process. It was the Europeans who devised a standard method for ending each computer statement with the semicolon (;). This standard is used today and recognized by most modern compilers. When a FORTRAN program was placed on an electromagnetic tape the separator between statements was the character count — every 80 characters a new card was being read. This is where the term “80-80 listing” came from. The computer would read a wad of punched cards and produce this listing, the contents of 80 punched cards (each 80 columns wide) printed on one computer page, tightly cramped together to save paper.

The international standards committees were quick to agree on what source code should look like. For example, it was agreed that the name of the variable that was to receive data should always be placed on the left side of an equal sign (or some alternative sign), and the mathematical formula that provided an answer for that variable should appear to the right of the equal sign.

Mathematical expressions were always enclosed in standard parentheses, and groups of source code statements (other than math) were enclosed in curly brackets or square brackets ({} or []). The standards came just in the nick of time — in the U.S. there were thousands of would-be programmers anxious to learn how to program with IBM, Burroughs, HP, and Honeywell compilers, assemblers, and computing machinery.

The FORTRAN language had some notable competitors in the 1960s, like Algol, the language invented by the Burroughs Corporation. The interesting thing about Algol was its predisposition to require the grouping of statements in packets that could only be entered at the topmost statement (one entry point, multiple exit points). Later we called this technique “structured programming.” All modern compilers use structured programming.

And then there was COBOL (Common Business-Oriented Language). This language used statements that were more like the English language than any other, and people loved it. It also had one other quality that the creators of FORTRAN wished they had included: the ability to bring character strings out of the mass of source code and deposit those strings at a convenient place outside of the code. What did this do for a corporation that used COBOL? Several variations of a basic program could be compiled and presented to the customer by making small changes to the list of character strings on an off-line computer. Even today you can incorporate a new bank anywhere in the world, call a corporation in Cleveland, Ohio, and get yourself a complete digital computer operating system for your bank by answering a long list of questions that define your bank precisely (like its name, etc.). The code used to produce this operating system is COBOL, which has been in place for 40 years.

The most widely used programming languages of the 1970s and ’80s all had one common attribute — they were always willing to steal any new ideas for better programming techniques from their competitors.

During this time IBM had committed to developing a personal computer or desktop computer that would provide to the individual employee some limited computing capability at his own desk. IBM found itself without a suitable operating system for this tiny machine, and a man named Bill Gates stepped up to the challenge and delivered the first Microsoft Corporation PC operating system in less than a year. When the PC became popular it hastened the death knell of the huge mainframe machines that IBM had created for two generations. Some saw this revolution coming, but most did not. There were no servers to connect groups of office PCs in those days — the communication links between the PCs and a central server did not exist.

While the hardware gurus were inventing new products every year, the software gurus were hard at work also. It was generally accepted that digital computers would become faster and more reliable every year, so the software gurus were able to look into the future and create compiler architectures that would serve these greatly upgraded computing machines.

Some of the laboratories in the U.S. and Europe began experimenting with languages more powerful than FORTRAN. They wanted more versatility in programming statements, easier ways to convey source code from place to place (other than shipping boxes of punched cards across the country), and programming methods that produced fewer errors in the final product delivered to the customer. In the 1960s and ’70s more time was spent in testing and proofing major programs than was spent in developing and coding the program.

In the early 1970s, a Bell Laboratories language called the C language began to appear in the computer software technical journals. There are a ton of funny stories about the C language that followed — we will repeat only one of them here:

The Bell Laboratories on the East Coast had an in-house programming language that they called the B language. It claimed a lot of advantages over the stiffly formulated IBM FORTRAN language, and the scientists at Bell were constantly proposing new and wonderful ways to make the B language more useful. For example, it eliminated all punched cards and read each source code statement directly from a file listing — three cheers for the semicolon! Then one day Bell Labs got a new leader. When he discovered how many people were spending time on the B language, he put out a sharply worded notice to the entire staff: “There will be no more work accomplished on the B language; this is a business environment, not a university.” Or words to that effect.

This announcement came out on a Friday, and according to a reporter at the New York Times, “On the following Monday morning the C language was born at Bell Labs.”

There is no reason to enumerate the programming advances that the C language offered to the software world in the 1980s. But it is notable that every one of these advances was incorporated into various forms of the FORTRAN language by the end of the 1990s (by every major computer hardware manufacturer, most of whom had their own brand of FORTRAN available for their machines). The FORTRAN language also borrowed ideas from COBOL and Pascal.

Then along came ADA, the language that borrowed the best from FORTRAN, COBOL, Algol, Pascal, and C (not to mention a whole raft of lesser-known computer languages that made their debut in Europe or the Pacific Rim countries). But ADA was doomed. Most federal and state government contracts that required computer programs as a deliverable required that the development be done in the ADA language. The American software industry therefore attached large charges to their software contracts because the language was new, untested, and required considerable training of its existing programming staff to use this newfangled language. ADA is a great language; it requires strong type casting of all its variables to eliminate a host of nagging errors that always persisted in the simple FORTRAN deliverables, and the ADA compiler that processed the individual statements to form an executable program was light-years ahead of any other compiler because of its error-checking capabilities during code development. But the C language quickly stole the thunder of ADA.

By this time structured programming had become a software industry hallmark — no more would irrational programmers be able to write “spaghetti code” that sent its tentacles into all parts of the operating system and data storage devices on a digital computer. This was the same structure that Algol hinted at decades earlier and ADA demanded.

The C language achieved wide acceptance around the world for many reasons — one possible reason was the fact that it was invented at an American laboratory and not by the federal government and Department of Defense in Washington, D.C. Soon the C language evolved into the C++ (C plus-plus) language (developed by Bjarne Stroustrup, of Bell Labs). C++ is an object-oriented programming (OOP) language. OOP compilers were the first to create packets of executable software and all the data to support that software in one place — the packets were movable or floatable in memory. At first it was not clear why C++ was necessary, but most C programmers immediately jumped into the new language so they would be able to program in the “latest and greatest” computer language. One advantage was that C++ added greatly to the programmer’s ability to manipulate strings of characters — something that was left out of the C language.

The object-oriented programming wave of influence began with a language named Smalltalk, which was invented at the Xerox Palo Alto Research Center. It would have made its mark much earlier in the 20th century if there had been PC and Macintosh operating systems that could float hundreds of millions of storage locations inside one machine — but this was not to be until Windows 2000 and the Macintosh 21st century compilers appeared at the local computer stores. With object-oriented programming the programmer had to learn one important word: new. Every time a code group was exercised within a program, that group was generated anew from a declared class of code packets, and the group was used only one time before it was set loose to float around in memory. Could such an action fill up memory on the worst of occasions? You bet! So what should the operating system provider do to combat this evil? Provide a garbage collector to grab up those loose pieces of code and data, cancel them out, and return the space used by the floaters to useful memory available to the program being executed. It sounds a little goofy, but the operating system gurus made it work.

How do the modern operating systems accomplish this sort of garbage collection task? I have no idea, but you will note (several chapters later) that every modern Microsoft compiler includes in its executables a resident garbage collector and you had better not remove it from your source code if you know what’s good for you!

When the 21st century began, Microsoft hired some of the finest computer minds in Europe and the U.S. and launched into the C# era (pronounced “C sharp”). The group was led by Anders Hejlsberg. A worldwide committee was appointed to propose international standards for this language that would become the new world standard.

Microsoft C# is not totally unique. For one thing, another American corporation, Sun Microsystems, released a computer language named Java that created intermediate code rather than machine code, and it achieved phenomenal acceptance by the business world almost immediately. Intermediate code is not machine code, so those who distributed Java programs had to include the Java cookie (a run-time language) to interpret the intermediate language product and produce executable (machine) code. Programmers who write in Java create the source code to generate intermediate code one time for all machines (PC, Macintosh, Sun, etc.). Then there is a separate Java cookie for each type of computer.

The Microsoft C# compiler also needs a cookie — a very large cookie. Today, if you own a PC or a Mac or any other desktop computer and you have ever hooked up to the Internet, then you have a considerable number of cookies on your machine that interpret incoming Internet code to produce images on your screen.

The success of Java hastened the introduction of the Microsoft C# compiler in 2000.

What does Microsoft C# promise to its users? These are some of the reports:

  • The intermediate language code that is generated by the Microsoft C# compiler will match up to a cookie on most desktop machines to produce executable code on that machine. The cookie is called a Common Language Runtime (CLR) package and is about 32 MB in size. The CLR package is part of the Visual Studio .NET Framework, which is designed to accept multiple languages in its integrated development environment (IDE), including Basic, C++, J#, and C#.

  • C# programs run slower than C or C++ programs because they include environmental checks that C and C++ do not make (for example, no writing to portions of the computer reserved for the operating system, etc.). Slower but safer.

  • C# accommodates object-oriented programming by including a built-in garbage collector (to keep memory clear of unused code and data).

  • C# never allows a variable to be used in the program until it has been initialized in some way.

  • The Microsoft C# compiler is the most exhaustive compiler ever written by Microsoft. It makes checks for proper type casting of every variable, similar to the advances made by the ADA compiler a decade ago. When a program is compiled and run in the IDE, it runs in a Debug mode as a default. This means that anytime program execution stops, the line of source code where the fault occurred is highlighted to show the programmer what went wrong. The error messages that the Microsoft C# compiler can produce during compile time seem to be uncountable.

  • During project development the compiler debugger checks every keystroke made by the programmer, and error messages appear immediately. This is a feature found a decade ago in the ADA compilers.

  • Future releases of the Microsoft C# compiler will include methods for immediate transfer of code and data to HTML format to enable program operability on the Internet.

  • The capability to handle multimedia is promised in future releases of Microsoft C#.

  • Because Microsoft C# is so tightly structured, it opens the way for third-party software corporations to produce ancillary software that performs checks on compiled code (to an even deeper level than the original Microsoft C# compiler) and prebuilt code that serves a particular industry (such as financial, communication, entertainment, computer-aided design, and transportation). In Chapter 3 you will be exposed to a toolbox of about 60 prebuilt code packets (each represented by an icon). Third-party software groups may expand this selection to 500 to 600 packets.

If you are a new programmer just entering the field, you will deal primarily with multimedia in the future. The day will come shortly when most corporations own little or no software. They will lease all their software from a central distributor (like Microsoft) who guarantees that the software downloaded by corporations will be the perfect software for its tasks. Once corrections to the software are made at the central repository, all users will have access to that corrected software that afternoon.

In the next chapter we will discuss how a user communicates with a digital computer.


Chapter 2: Personal Communication with a Digital Computer

Overview

Even in the earliest days of the computer revolution, our computer forefathers were quick to recognize that the human-to-computer interface was going to be the really tough nut to crack. Each group had its own agenda for how this interface should be constructed. They knew that the lack of standards and the strong desires of dominant groups in the software world could result in the proliferation of lots of bad ideas. This had happened many times in the hardware world, with predictably bad (and expensive) repercussions.

For example, when the electric lightbulb emerged in the 1900s as the most desirable means for illuminating a room, there were over 160 configurations proposed for how the metallic end of the bulb should look (and be placed into a receiver that provided the electrical power). It took General Electric and Westinghouse 20 years to outflank all their competitors and enforce a standard for lightbulbs that screwed into a porcelain and metallic receptacle to produce light.

In the computer world IBM took the lead and called most of the hardware shots early in the game. By 1980 Microsoft began to call all the shots in the software game. It didn’t take an IBM or a Microsoft to see that the principal means of communicating with a computer should be the keyboard.

There were some disagreements on the layout of the keyboard. After all, the original typewriter was created and patented to provide a means for blind people to communicate with the outside world one keypunch at a time. Once these blind people learned the layout of the keyboard they could write letters to their heart’s content — so long as the recipient of the letters was a sighted person. The notion of a more modern keyboard layout lost most of its momentum when the United States Congress refused to intervene in the free economy process and dictate a newer, standard layout for the keys on a keyboard.

In the 1970s only one in four high school students learned to type on a typewriter — they were the people destined for the business world. Today all high school students who are sighted and have two hands learn how to type or “keyboard.” It is a necessary skill for employment in the modern world.

Loading a program into a small computer and running it in the 1970s was a real adventure. For example, at the U.S. Air Force Academy Aeronautics Department a small Honeywell computer was purchased to handle data taken from one low-speed wind tunnel and one supersonic tunnel that the cadets used in the aeronautical laboratory. The computer would reduce the data from the pressure transducers in the wind tunnels and produce data that showed how a particular aerodynamic shape performed inside the tunnel. To write the program that reduced the data, a keyboard was used to produce a stream of characters that were written to a paper-tape puncher, which produced a roll of paper tape full of small holes. The compiler was loaded onto the Honeywell computer from a data tape, and the paper tape was fed into the computer to convert source code to machine code. Once the machine code was created (and placed onto a second paper-tape roll), the compiler was removed from the computer.

The second paper tape was read into the Honeywell machine to load it with machine code. Any area in memory that was not used by the machine code version of the data reduction program was available for data storage. All of this loading and unloading was necessary because memory in the Honeywell was too small to contain a compiler, machine code, and space for data at the same time. Once all this was done the machine was ready to crunch numbers. Once we got the program to work we never changed its source code and we kept the second paper tape (the one with the machine code) in a secure place in the lab — the thought of losing it and having to redo the whole process was more than we could stand.

With the advent of a digital computer processor on a machine with sufficient memory (enough to store a compiler, source code, and space for the machine code that was produced by the compiler), most of the agony of data processing went away.

Meanwhile a new use for small computers was devised in the business and scientific communities — a class of software called “word processors.” Since there was no windowing on the screens and no mice in the window, the best that a digital computer could do was to present one line of text at a time (or several lines that wrapped to the next lower line when the upper line was full of text). The keyboard was fitted with a series of function keys across the top to aid the word processing tasks. The smart operator learned a myriad of combinations of function keys plus the Alt key, Shift key, or Ctrl key, etc. A learned operator could move about the text with lightning speed using function key commands to move to the next paragraph or the next page, change text fonts, correct misspelled words, insert new text, or delete text. It was primitive, but it worked.

The secret to efficient word processing at that time was to not make a mistake when you typed in the text in the first place. It was the word processor’s needs that finally brought the issue of alternative inputs to a digital computer to the fore. The standard computer keyboard with all the function keys was simply not enough. But what to do?

It was the Macintosh computer that first demonstrated the capability to produce a screen with a white background and black letters on the foreground. They did not use the term “windows,” as the term was already patented by Microsoft. But both competitors went at it hot and heavy — the Macintosh had great graphics (and continues to excel in that arena) and the PC had great compiler support in its FORTRAN language. Then Macintosh invented the mouse and for the first time in computer history the user could traverse freely across the screen with a mouse cursor and accomplish all kinds of wonderful computer tasks. Microsoft followed suit quickly. The concept of clicking on a particular spot on a window to cause some task to execute was almost magical!

Once the combination of windows with a mouse cursor was perfected, the word processors became 100 times more efficient, and software manufacturers predicted that there were more efficiencies to follow.

It is undetermined who invented the first control; it was probably a forerunner of the push button that allowed the user to call up another screen temporarily. It is also undetermined who invented the main menu that is now displayed across the top of every commercial software program in existence today. Word processor users wanted as large a screen as possible to type their words, and the placement of help tools around the edges of the screen decreased the size of the word-processing space. The main menu with its pull-down submenus eliminated this dilemma. But the main menu with pull-downs had to await the birth of the mouse to be effective.

History will record that the software gurus of that era were champing at the bit for increased capability for their software while the architects of operating systems were far behind in their pursuit of computer excellence. The architects of operating systems will in turn point to the hardware gurus who were not cooperating — the hardware systems were incapable of addressing sufficient computer memory to accomplish the great feats that the software applications teams envisioned. Recall that, as late as 1998, a PC or Macintosh owner who replaced the small, original equipment hard drive in his machine with a much larger hard drive was forced to create multiple drive sectors on that hard drive (drive C:, drive D:, drive E:, etc.).

The PCs and Macs originally provided only 16 wires for addressing memory, and 16 wires can only address 216 addresses (65,536 addresses, commonly called 64 K). This is the reason why computer programming prior to the 1990s was limited to 64 K of region — the computer could not process a program whose source code occupied more than 64 K. Some Macs limited the source code region to 32 K. If this limit of 32 or 64 K was reached, then the programmer had to break up the program in smaller pieces to compile and run it.

In 2000 both PCs and Macs confronted the issue of addressing large areas of memory by creating operating systems that could address hundreds of millions of addresses. No longer was it necessary for example, to divide a 100 MB hard drive into multiple sectors like drives C: through ZZ:.

The creation of cheap random access memory (RAM) and read-only memory (ROM) made life even better for computer users because their computers could access large amounts of code at computer start-up and refrain from reading or writing to the hard drive most of the time. This made programs run faster because the time required to access the hard drive is eight to ten times longer than RAM/ROM access.

Meanwhile, the clocking speeds of CPUs increased dramatically, from a nominal 10,000 cycles per second in the early 1960s to 4 to 6 million cycles per second in the late 1990s.


Are There Better Ways to Enhance the Person-to-Digital Computer Interface?

If you and I were smart enough to answer this question, then we are wasting our time at our present occupation. Today the concept of the mouse has been implemented in just about every imaginable fashion — we have roller mice, laser mice, roller ball mice, and even baseball hats fitted with acceleration sensors that cause the cursor to move up, down, left, or right based on the movements of the wearer of the baseball cap. The rationale for the baseball cap is that the user does not have to lift his hands from the keyboard to move the cursor.

Don’t count out the touch screen advocates. There are truck stops in Nevada where a truck driver can touch a large map of the state of Nevada and the area he touches is expanded greatly in scale until the screen gets down to a 2.5-mile picture of the road conditions under one’s thumb. A red road is ice; a blue road is nice. Or there are suitcases that the maintenance people on a nuclear submarine carry around inside the boat. By continuing to point on the screen of the video terminal inside the suitcase the scale expands to show more and more detail of the hardware embedded in the hull. Finally the screen presents a tiny portion of the boat, and when the mechanic points to a particular subassembly that needs repair he is informed if that part is located onboard. If not onboard, it is ordered for pickup the next time the submarine surfaces. Before the suitcase came along, the mechanics carried 1,800 pounds of user’s and maintenance manuals somewhere in a forgotten corner of the submarine. The suitcase and all its data modules probably weigh a hundred pounds. This means that the seamen aboard the boat can now store 1,700 pounds of DVDs to help pass time under the seas.

I challenge you to discover the next generation of input devices to digital computers.


In Conclusion

What does all of this computer activity mean to you, the programmer of the 21st century? These are the most important personal inferences if you choose to enter the programming world:

  • With the advent of true structured programming and true object-oriented programming, combined with the Microsoft IDE as posed in Microsoft Visual Studio .NET, the average programmer will be relieved of the responsibility of generating 90 to 95 percent of the source code that he would have generated 30 years ago. The focus of computer programming efforts at software corporations will turn to combining existing packets of code into usable end products that make life easy for the user of the software (the customer). Whatever task a digital computer program is asked to do, there will probably be a prebuild that meets the need.

  • These prebuilds will be presented to you, the programmer, within the confines of the IDE, and it will be necessary that you learn how to use them. The size of computer programs will not be an important factor in their design in the future. You will spend less and less time in the actual coding of software to meet the buyer’s requirements and more and more time investigating the availability of prebuilt code that will do the job for you (with no necessary long-term testing of deliverable products).

  • The number of computer programmers who will be gainfully employed in the preparation of commercial software will decrease somewhat in the next 20 years because the productivity of individual programmers is expanding so rapidly. Those who migrate to the large corporations (like Microsoft, Borland, Oracle, Computer Associates, Corel, etc.) will find the greatest opportunities to expand their knowledge of the industry. Smaller software companies are constantly being bought out by larger companies.

  • Many of the programs that were written in the 1980s to meet specific requirements of software buyers will now be met by wiring together the capabilities of more basic computational and data processing machines, like databases for example. Database experts will revolutionize the financial and banking industries, if they have not already done so.

  • The federal government continues to be the biggest buyer of new software for its agencies, particularly the Department of Defense. Only the federal government is willing to invest large sums of money to reinvent large software programs every decade — commercial business will not spend these moneys for perceived gains in productivity in the workplace. If the federal government goes out of the software procurement business, American programmers will suffer greatly.

In the next chapter we discuss the main platform in which you will work — the Microsoft IDE.


Chapter 3: The Microsoft Integrated Development Environment (IDE)

Overview

The IDE is a one-window Microsoft Visual Studio home for the potential programmer, set before her to create C# projects. The version 2.0 IDE window has been greatly simplified over the past three years (since versions 1.0 and 1.1) to increase programmer productivity and reduce redundancy. The old 1.0 home window provided too many ways to perform similar tasks and overpopulated the presentations.

You cannot create C# projects without a C# compiler — either the Visual Studio 2005 Home version/Professional version or the C# 2005 Express Edition. The Visual Studio Professional version occupies almost 3 GB on one’s hard drive, while the abbreviated Express Edition occupies only 200 MB. The Express Edition was built for students, but it meets the needs of 80 percent of the programming community. There is little reason to purchase the full Visual Studio 2005 unless you write source code in multiple computer languages or you deal in projects written for international Internet transactions. With the full Visual Studio 2005 you also have the capability of including legacy code written in C++, Basic, and J#. The Express Edition is sold as a stand-alone compiler.

During the installation process, the Microsoft software creates a holding area at C:\Documents and Settings\Owner\My Documents\ Visual Studio 2005 for your projects.

Every project in this book can be built, compiled, and run with any of the three compilers (Professional, Home, or Express). However, we chose to explain the relevant features of this new compiler in terms of the full Visual Studio 2005 Home Edition because it is the type of compiler that most Microsoft-class programmers are accustomed to. If you prefer to concentrate on honing your computer software skills on only the dominant language, C#, then the Visual C# Express Edition is your best (and least costly) bet.

All three of these C# compilers use the new MSBuild format for file creation and storage within a project. MSBuild was finalized in 2004 and incorporates a system that recognizes old C# project formats from versions 1.0 and 1.1. It immediately converts them to the new version 2.0 2005 formats. Before this conversion is made, MSBuild asks the programmer if the old-format project folder should be saved in its original form. If the programmer chooses to do this, the old project is copied to a specific subdirectory in C:\Documents and Settings\Owner\ My Documents\Visual Studio 2005\Backup Files.

There is no mention in the IDE instructions about the powerful version 2.0 built-in project converter. You can place an old-format Visual Studio C# project into the new-format storage area and load it into the new IDE for rework, compilation, and execution. The conversion software works flawlessly and produces a Visual Studio C# version 2.0 project. So don’t be afraid to load old-format Visual Studio C# projects into this new 2.0 system.


Housekeeping Tasks

It is time to perform two housekeeping tasks before you proceed any further:

Task 1: Create a Shortcut

When you install one of the new 2005 Visual Studio version 2.0 C# compilers on your PC, there is no provision for placing a shortcut icon on your desktop. To create one, follow these steps:

  1. On the desktop, double-click on My Computer to bring up the list of drives on your computer. Click on the C: hard drive, which brings up a list of the major subdirectories on the drive.

  2. Double-click on Program Files to display a large number of folders. Double-click on the Microsoft Visual Studio 8 folder to reveal several principal folders that support all compiler languages in the new 2005 Visual Studio suite.

  3. Double-click on Common7, then on IDE.

Visual Studio 2005 (Home or Professional Edition): Within the IDE subdirectory, right-click on the devenv executable file, which is about 212 K. devenv contains the IDE main window. A pop-up menu appears; click on Create Shortcut to create a shortcut at the bottom of the existing list of files and folders in the IDE subdirectory. Drag the shortcut to the desktop and name it something like Visual Studio 2005 or Visual C#. This icon will be your entry point into the IDE.

C# 2005 Express Edition: Right-click on VCSExpress.exe, which is about 178 K. VCSExpress.exe contains the IDE main window for this edition. This file is easy to spot because it has a tiny green icon to the left of the filename. A pop-up menu appears; click on Create Shortcut to create a shortcut at the bottom of the existing list of files and folders in the IDE subdirectory. Drag the shortcut to the desktop and name it something like C# Express. This icon will be your entry point into the IDE.

During installation, all three of the new 2005 C# compilers will create a subdirectory within My Documents to house the projects you create. The subdirectory is C:\Documents and Settings\Owner\My Documents\Visual Studio 2005\Projects. This is the subdirectory that the IDE defaults to whenever the programmer chooses to open an existing or new project.

Task 2: Display Extensions

If your PC is set to display filenames without the extensions, this will hamper your work in the IDE. You can reset your PC so it displays full filenames with extensions by doing the following:

  1. Move to the desktop, click on My Computer, then on <Owner’s> Documents.

  2. Click on Tools in the main menu, Folder Options in the submenu, and the View tab at the top of the Folder Options window.

  3. Move downward in the Advanced Settings list in the lower half of the window to the Hide Extensions for Known File Types item. Uncheck this item so all the file extensions will appear on your filenames.

  4. Press Apply and then OK to exit.


Version 2.0 Visual Studio Major Subdirectories

When you click on the My Documents shortcut on the desktop (which the XP operating system provides when it is installed on your computer), you are immediately offered Visual Studio 2005 as one of the subdirectory options (it was placed there when the compiler was loaded).

Once you click on Visual Studio 2005, you are presented with six subdirectories: Backup Files, Code Snippets, Projects, Settings, Templates, and Visualizers:

  • Backup Files: This is the storage location for any old C# 1.0 or 1.1 projects that you choose to place in the Projects subdirectory for inclusion with the new version 2.0 compiler. During the conversion process in the IDE you are asked if you want to keep the old version intact for possible later use. If you answer Yes, the old project is copied into the Backup Files folder in its old format.

  • Code Snippets: This is a storage location for miscellaneous pieces of code that you create.

  • Projects: This is the largest storage area in Visual Studio 2005. All your projects are sent here (unless you choose to route them to a different location). If you tend to create lots of projects, you may want to consider partitioning this subdirectory from the very beginning. For example, the sample projects in this book are placed in a subdirectory named DemosSourceCode.

IMPORTANT

Once you create a project, the storage location of that project is fixed for life. If you move projects around on your hard disk after their creation, you defeat the pathing instructions within the project. Don’t move projects around once they have been created!

  • Settings: These settings are written by the operating system. You will never manually enter data here.

  • Templates: These project templates are created by professional programmers for repeated use.

  • Visualizers: This is a storage location for new features that Microsoft intends to provide at a later date.

The companion files add two subdirectories to this list: Tagged KT-Demo Code and User Docs.


A Closer Look at the Projects Folder: MSBuild Project Files

The architects of the IDE chose to give programmers two choices/formats for storing project files. You must make your choice at the time you create a new project in a window that looks like Figure 3-1.

Image from book
Figure 3-1: New Project window

When the programmer accepts the default “Create directory for solution” option at the lower right of the screen, this is the way the files appear for a project named Thumbnail222:

  • Top level: One directory named Thumbnail222 and one file named Thumbnail222.sln. To initiate this project, the programmer clicks on the Thumbnail222.sln file, the solution file. The Thumbnail222.suo file is a computer-generated, machine code file.

  • Thumbnail222 directory: If the programmer clicks on this directory at the top level, this file listing appears:

    • bin directory

    • obj directory

    • Properties directory

    • Form1.cs file

    • Form1.Designer.cs file

    • Program.cs file

    • Thumbnail222.csproj file

    • Thumbnail222.csproj.user file

If you create project ThumbNail333 and uncheck the “Create directory for solution” option in the New Project window, the result is:

  • Top level: Three directories plus seven files appear:

    • bin directory

    • obj directory

    • Properties directory

    • Form1.cs file

    • Form1.Designer.cs file

    • Program.cs file

    • ThumbNail333.csproj file

    • ThumbNail333.csproj.user file

    • ThumbNail333.sln file

    • ThumbNail333.suo file

To initiate this project, the programmer clicks on the ThumbNail-333.sln file, the solution file. This file’s location formulation places all the files that were included into one directory level rather than the two directory levels (created when you choose “Create directory for solution”).

Warning

Once you have selected an option, live with it, at least for the present project. If you move the solution file (ThumbNail XXX.sln) into a different directory tier (manually), all the pathing in the project goes awry.

One reason to choose the “Create directory for solution” option is so that you are confronted with only one file when the project is first opened — the solution file, which is the one you click on to open the project. (The *.suo file is a machine-language file that belongs to the compiler — you never touch it.)

If you allow all files to be placed on one directory level, then you have to pick out the solution file and click on it to open the project. Fortunately, the solution file will always be the next-to-the-last file near the bottom of the list.


More Detail on the Project Files

To discuss these files, we will assume that the “Create directory for solution” option was chosen (check box checked), which places these files into three different directory levels.

These are the important files in an MSBuild project folder (for demonstration purposes, we will name our project HokeyPokey):

Level One: Project name, HokeyPokey. In earlier versions of Visual Studio C# the name of the project could not contain blank spaces. This has been corrected in version 2.0. For example, in this case, after you name the project Hokey Pokey, the project files carry the namespace as Hokey_Pokey.

Level Two: There is one subdirectory, which has the same name as the project (HokeyPokey), and one individual file named HokeyPokey.sln. The *.sln file is a solution file and contains the name of the project and information about the form of the debug and release versions of the project executable. This file also sets some internal switches in the compiler. To bring a project into the IDE, it is necessary to double-click on the *.sln file. Then the *.sln file calls the *.csproj file, which is located in the adjacent HokeyPokey subdirectory. HokeyPokey.csproj is the entry into the main project.

Level Three: These are the subdirectories and files contained in the HokeyPokey directory, which is a subdirectory of HokeyPokey.

This level has three subdirectories: bin, obj, and properties. It also includes five individual files: HokeyPokey.csproj, HokeyPokey.csproj.user, Form1.cs, Form1.Designer.cs, and Program.cs. The HokeyPokey.csproj and HokeyPokey.csproj.user files are the link between the IDE and the project. They are computer-generated files that are never modified by the programmer.

As has been the custom for many years, the Form1.cs file is the source code file. Form1.Designer.cs holds the descriptors for the controls placed on the form, which is the representation of one Microsoft window. The Program.cs file is part of the entry point for the project and should never be modified. Leave it alone.

If you want to view any of these files, just open them in WordPad. They are text files. One of the stated goals of MSBuild was to place the important project files in the project in text format (as opposed to machine code format) for ease of reading by the programmer.

In summary, everything that the programmer does in the IDE is reflected in the Form1.cs or Form1.Designer.cs files. The programmer types some of the entries into the source code file, Form1.cs, while other entries are computer-generated. The IDE places all code in the Form1.Designer.cs file (window template) to describe the items the programmer has placed onto the basic Microsoft window template (labels, buttons, text boxes, etc.).

The obj and properties subdirectories are used only by the compiler — the programmer makes no entries in any of the files here. But the bin subdirectory contains all the executable forms of the project, including HokeyPokey.exe, HokeyPokey.pdb, and HokeyPokey.vshost.exe. The only executable you will use at this time is HokeyPokey.exe.

IMPORTANT

If your project reads any programmer-created text files during its execution, you have two choices in the pathing of the files (so your project can find the file to be read). The first method is to include the entire absolute path in the name of the file and place the file anywhere you want on your computer. Absolute pathing always begins with “C:\.” The second method is to place the file to be read in the same subdirectory as the executable file. During preliminary work on a project, it may be easier to place a file that is to be read into the project in the same subdirectory as the executable, and then convert to absolute pathing just before code release. In this case, the directory in which the executables are found is HokeyPokey\HokeyPokey\ bin\debug.

Examples of absolute pathing are shown in the KT series of projects installed from the companion files, where the data files are all located in a C:\Program Files\KT folder.


A First Look at the IDE Main Window (Home or Professional version)

Click on the Visual Studio C# compiler shortcut that you created earlier on your desktop to bring up the IDE. The first window presented to you is the Microsoft IDE Start Page, shown in Figure 3-2.

Image from book
Figure 3-2: Microsoft IDE Start Page

Note the main menu across the top of the window. Click on File in the main menu, followed by New and then Project. The New Project window opens, as shown in Figure 3-3.

Image from book
Figure 3-3: The new HokeyPokey project

Every project we discuss from here to Chapter 24 uses the C# language with the Windows subchoice (in the Project types column). In Chapter 25, when we demonstrate how to build a deployment project, we will choose the Setup and Deployment option from Other Project Types.

Having picked a computer language (C#) and a project type (Windows), we next pick a template: Windows Application. As soon as Windows Application is highlighted, the project Name and Location text boxes are enabled. In the Name text box we enter HokeyPokey; this information immediately appears in the Solution Name text box. If you leave blank spaces between the words in the name of a project, the IDE replaces the blanks with underscores (so “Hokey Pokey” would become “Hokey_Pokey”).

The default location of your projects is C:\Documents and Settings\Owner\My Documents\Visual Studio 2005\Projects. However, you have the option of extending this by clicking at the end of the default location and adding a subdirectory to the path (e.g., \KT101). Whatever you type as the project path this time will appear in the Location text box the next time you begin a new project.

Be sure the “Create directory for solution” option is checked.

Click on the OK button at the bottom of the window to begin the HokeyPokey project.

There are numerous ways to display this IDE window, which is unnecessarily confusing. However, once you have set up the IDE the way you like it, the format will be remembered by the IDE software and copied each time you open a new project.

The important elements in the IDE are the Solution Explorer, Properties window, Toolbox, and source code, as discussed below.

  • The Solution Explorer should occupy the far-right edge of the screen. This listing of the various elements of the project provides an easy way to access them and bring them to the forefront of the window.

  • The Properties window normally occupies the far-left edge of the screen (or it can be placed below the Solution Explorer). This window is a combination of two windows — one that displays the properties of a form or control that is currently highlighted in the window template and one that displays the prebuilt source code (events) that may be executed when the user clicks the control. To toggle from properties to events, the programmer must click on the Events icon (the bolt of lightning at the top of the window). To return to the Properties window from the Events window, the Properties icon (to the left of the bolt of lightning) must be clicked.

  • The Toolbox contains all the prebuilt code for all controls. It is shown as a tab on the left edge of Figure 3-4. It must be placed on the screen to continue work on the project.

    Image from book
    Figure 3-4: The HokeyPokey project in the IDE main screen

  • The source code listing supports the Form1 template. Since the window template and the source code both occupy so much room in the IDE window, the programmer is forced to display one or the other (not both) in the center of the screen. The F7 key on the keyboard toggles between the Form1 template and the source code. The items in the center of the IDE window are also equipped with tabs at the top (just below the main menu and toolbars). There is a tab for each window template (FormX.cs[Design]) and source code (FormX.cs).

    If you create several forms in a project, the tabs tend to cover one another, so there is an alternate way to choose what to display: At the top-right corner of the window template area there is a down arrow. If you pass the mouse over the down arrow the words “Active Files” appear. Clicking on this down arrow presents you with all the files that are actively present in the IDE, and you can choose the one you want to display.

    You do not want to click on the black X just to the right of the down arrow — this is the mechanism for closing the active file in the center of the IDE completely.

Since the Properties window, the Solution Explorer, and the Toolbox all occupy so much space on the IDE, it was necessary to invent a way to hide these subwindows to provide more space for the source code listing.

Note that there are pushpins in the HokeyPokey IDE window in Figure 3-4. In the Properties window (on the left) and the Solution Explorer (on the right), the pushpins at the top of their respective windows are vertical. If you click on a pushpin, the window immediately hides at the left or right edge of the IDE screen and the name of the window is shown in a tab where the window disappeared. This way, more room can be provided for the window template and the source code listing. If you click on the tab at the left or right edge of the IDE screen, the window that you hid reappears, but the pushpin is in a horizontal position. The only way to keep this window displayed for an extended period of time is to click on the pushpin so it turns to a vertical position.

To operate a project properly in the IDE window, you must have access to the Toolbox. If the Toolbox is not present, click on View in the main menu, followed by Toolbox. The Toolbox will immediately appear adjacent to and partially covering the Properties window, as shown in Figure 3-5.

Image from book
Figure 3-5: Adding the Toolbox

We now have the Toolbox and the Properties window competing for the same space. One way to solve this dilemma is to move the Toolbox to the lower right of the IDE screen, below the Solution Explorer. But this requires that we reset the Toolbox window’s appearance option from Dockable to Floating, drag the window to a point below the Solution Explorer, convert its appearance option back to Dockable, and dock the window to the right edge of the IDE screen.

Warning

If you choose Tabbed Document for the presentation form of any subwindow in the IDE screen, the only way to change to another form of presentation is to place the cursor over the subwindow and right-click to change the presentation form.

Another option is to let both windows occupy the same space by hiding one of them. To do this, highlight a window (Properties or Toolbox), click on the pushpin at the top of the window to make it horizontal, and allow the window to hide, leaving its tab displayed along the edge of the IDE screen.

It would have been nice if Microsoft offered some basic layout for the IDE screen, one that the programmer could pick by clicking on an icon at the top of the IDE window. Then the important elements of the project (window template, source code listing, Toolbox, Properties window, and Solution Explorer) could all move to their appointed places.

We normally keep the Toolbox and the Solution Explorer hidden at the left and right extremes of the IDE window, but we keep the Properties window open since the properties of every control that you highlight in the window template must be displayed here. If you are creating a window template with numerous controls on the window, you may want to open the Toolbox and affix its pushpin in the vertical position (to keep it open) until all controls have been placed on the window template. Then you can hide the Toolbox — you are finished with it. You will always need a wide space in the center of the IDE screen to view the source code.

At this time, the Properties window is in partial view at the left edge of the IDE screen (partially hidden by the Toolbox), and it will remain there because its pushpin is in a vertical position. To uncover the Properties window, click on the Toolbox pushpin to hide it at the left edge of the IDE screen. There should be plenty of room to display the window template or the source code that supports the window template. But if you need more room, click on the pushpin at the top of the Solution Explorer and hide it as well.

Main Menu

The main menu and toolbar at the top of the IDE window include the standard tools for performing all file management functions. They are: File, Edit, View, Project, Build, Debug, Data, Tools, Test, Window, Community, and Help. File is used primarily for project creation, Edit has little or no use in the IDE window, View allows the programmer to add or change subwindows in the IDE screen, Project allows the programmer to add elements to the project, Build builds the project, and Debug runs/executes the project. Data, Tools, Test, Window, and Community are rarely used. The Help menu item can be immensely useful if the programmer remembers to install all of the help files on her PC when the compiler is loaded. Searching the MSDN files on the Internet tends to be an unforgiving process because there is tons of data out there that you cannot use.

Disappearing Subwindows

If any IDE subwindow suddenly disappears (and these sorts of things do happen), you can always bring the window back into the IDE by clicking on the View main menu item. Then click on the window you need in the submenu list to display it on the IDE.

Properties and Events

A property of a control or form (window) includes such items as size, position within the screen, color, fonts, font sizes, etc. An event is associated with a control and causes something significant to happen in the sequence of events. The event occurs when the user of the software either clicks or double-clicks over a specific control. For example, when the user clicks on a button (the most common control in modern Windows programming), the event that has been prescribed occurs. Usually, clicking on a button brings up another window. The code that produces that window is the event. Every control has a default event, and the form of the event is placed in the source code as soon as the programmer highlights the control in the window template and double-clicks it. That is, as soon as the programmer double-clicks on a highlighted control, a default procedure is placed in the source code to support that control.

If only default events were needed, the programmer would never need to use the list of events that is displayed as soon the Events icon is clicked (double-clicking on the control in the window template would create source code for every event). But there are many events that are not the defaults, and instantiating these events requires the Events subwindow.

It is possible to add two event handlers to a particular control: one that is executed every time something happens within the control and one that is executed after the control is exited. For example, say you have a text box control. Each time a keystroke is recorded in the text box, the ASCII character entered is examined by the Text Change handler. A second handler named Leave may be executed only after the text box control has been exited.


Opening a Project in the IDE

To open any existing project in the IDE (or create a new project) the programmer first clicks on the File main menu item at the top left of the IDE screen. These submenu choices are offered: New, Open, Save All, Source Control, Recent Files, Recent Projects, and Exit. We are only interested in New and Open. New creates a new project, and Open opens an existing project.

Creating a New Project

To create a new project, follow these steps:

  1. Select File from the main menu, then choose New. You are offered three choices: Project, File, or Project From Existing Code. The File option is of little use, and it is unclear what the purpose of the Project From Existing Code option is. It looks like you should be able to clone an existing project here (and rename it so it can be reworked and used in repetitive projects), but that does not work. Chapter 4 shows how to clone a Visual Studio C# version 2.0 project. If you create projects that are similar in their makeup, then this manual clone sequence will decrease your preparation time.

  2. Under New, click Project to open the New Project window, as shown in Figure 3-6.

    Image from book
    Figure 3-6: New Project window

  3. The first thing you must do is name the project. In the Name box near the bottom of the window, replace “WindowsApplication1” with Brave New World. As soon as you type the new project name, the words “Brave New World” will appear in the text box after Solution Name.

    Note that the “Create directory for solution” option is checked.

  4. The second entry you may have to make is in the Location box directly below the Name box. If you highlight the path shown in Location you can add a subdirectory below the Projects subdirectory. For example, if you are creating a project named Scooby Doo and you wish to place its project folder in subdirectory WhereAreYou, enter Scooby Doo in the Name line and \WhereAreYou after Projects in the Location line. The subdirectory that you name in the Location box need not exist when you create the new project. If it does not exist, the IDE will create that subdirectory and place the new project there.

    In this case there is no required pathing after Projects in the Location box shown in Figure 3-6.

  5. Now click on the OK button to create the new project.

Note

If you choose a name for a project that includes “forbidden” characters (like #), a message will appear to tell you that the character is a no-no. The only two common characters (other than letters of the alphabet and numbers) that the IDE allows within project names are the hyphen (-) and the underline (_).

Figure 3-7 shows how the project looks initially.

Image from book
Figure 3-7: Brave New World project in the IDE

Note that the Toolbox window is hidden from view (its tab is shown at the left edge of the IDE window). The Solution Explorer window is on the right, and the Properties window is on the left. But you are going to need both the Properties window and the Toolbox in this demonstration (flip-flopping between the two), and they are both docked to the left side of the IDE.

The easiest way around this problem is to hide the Solution Explorer by clicking on its pushpin, which leaves a lot of empty space on the right side of the IDE screen. Now, when you click on the Toolbox tab on the left edge of the screen, there is room for both the Properties window and the Toolbox at the same time since the window template will move to the right to accommodate the needs of the two windows on the left.

Figure 3-8 shows how the IDE screen should look after you have hidden the Solution Explorer and placed both the Properties window and the Toolbox on the left.

Image from book
Figure 3-8: Rearranging the IDE

  1. Single-click on the window template Form1; the Properties window will be filled with Form1 properties. Note that Text is highlighted in the Properties window.

  2. Replace the word “Form1” next to Text with Brave New World and click on the window template. “Form1” at the top of the window (in the title bar) is replaced with “Brave New World.”

  3. In the Toolbox window, click on the Label control, then move the mouse cursor over the window template and click the mouse again. The label is placed on the window template and the word “label1” will appear inside the label (in the window template). In the Properties window the Text entry should be highlighted. Replace “label1” with Welcome to this Brave New World! Remain in the Properties window. Set the Location of label1 as (40, 34). Find Font, click on the + sign to expand the font details, and change the font size from 8.25 to 10.

  4. Return to the Toolbox. Click on the Button item in the Toolbox, and move the cursor to the center of the window template. Click again, and a button will appear there. Move to the Properties window of the button and replace the text “button1” with Push Me Now. Since the words “Push Me Now” won’t fit into the button width, drag the right edge of the button on the window template until all the text fits on one line (or move to the Properties window and set the button size to (110, 25)).

  5. Highlight the window template (Form1) and set its size to (300, 200) in the Properties window.

  6. Highlight the window template again, then place a second label from the Toolbox below the button on the template. Replace the text “label2” in the button Properties window with Good Grief, Charlie Brown! Set the location of label2 as (114, 125), and set the font size of the label to 10.

  7. The last task you will perform in this new project is to connect the Push Me Now button to the text “Good Grief, Charlie Brown!” such that the text changes when the button is pressed. To do this, you must create an event handler that is inserted into the source code to control the actions of the button. Highlight the button in the window template. You do not need to toggle to the Events subwindow (the one on the flip side of the Properties window) because the event you are about to install is the default event. To create this default event, simply double-click on the button in the window template (it is already highlighted). This creates an event in the Form1.cs source code named button1_click. As soon as you complete the double-click, you are shown the new code. The link between the code in the source code list and the button on the window template is made automatically by the compiler.

    IMPORTANT

    Double-clicking on any active item in the window template will only create its default event. We will show you later how to create events that are not the default. Don’t double-click on anything else in the main window — every time you do, some unwanted event handler will pop up in the source code.

  8. Within the curly brackets of the button1_click procedure, type this line of code:

label2.Text = "Row, row, row your boat.";

Q: | How do I switch back and forth between the window template and the source code view? | The F7 key toggles the view between code view and window template.
Q: | If I inadvertently drag a control onto the window template that I do not want, how do I get rid of it? | Image from book
Q: | Suppose I create an event handler, then decide later to delete it. How do I do that? | Image from book
Q: | I have opened the Events window, and now there is no way to get back to the Properties window (since only one of the two can be displayed at any one time). What should I do? | Image from book
Q: | If I create so many forms (windows) in a project that they all cannot have their tabs displayed at the top center of the IDE, how do I retrieve forms that have no tabs? | Image from book

Answers

Q: | The F7 key toggles the view between code view and window template.
Q: | Right-click on the unwanted control to produce a floating (context) menu. Click on Delete to delete the control.
Q: | Highlight the control (to bring its properties into the Properties window). Click on the Events icon (the bolt of lightning) to produce a list of possible events. The event that you want to delete should be highlighted in blue. Right-click on the event to produce a floating menu with two choices: Reset and Description. Click on Reset to remove the procedure from the code. (This method works only if the procedure is empty. If you have entered code in this procedure, then it won’t disappear from the source code list, but its hooks to the project will. So you must manually delete source code if it contains new code added by you. Perform this manual deletion after you have reset the event in the list of event handlers for the control.)
Q: | Move the cursor to the left of the Events icon where there is a small icon for the Properties window. Click on that icon to exit events and return to properties.
Q: | The number of tabs that can be shown on the IDE is usually no more than three or four. So, the designer of the IDE placed a down arrow at the top right of the center screen (just to the left of the black X, which one clicks to close the center screen). When you click on that down arrow, you are presented with a list of forms in the project, and you can bring any one of them into the center screen by clicking on the appropriate form.

Building and Executing Brave New World

If you don’t like the results of this creation process, move to My Documents\Visual Studio 2005\Projects and delete the project Brave New World. Now you can start all over again.

If you are satisfied with your creation, then it is time to compile and run the project. The IDE screen should look something like this:

Image from book
Figure 3-9: Complete Brave New World project

  1. We have not saved any of this work so far, so choose File on the main menu and click on the Save All item.

  2. Next, click on the Build main menu item. There are a number of submenu items, but the only ones that you will use are Build Solution and Rebuild Solution. Note that Build Solution has a shortcut key combination of Ctrl+Shift+B. You may elect to use this shortcut to do all of your project building. As soon as you elect to build, the source code will be displayed in the center of the IDE. The line number in the code where the cursor is presently located will be displayed at the bottom of the IDE, along with the cursor location within the line (but only the line number is important). If the error list at the bottom of the IDE screen shows an error in Line 33, for example, then you can use the up arrow or down arrow on the keyboard to move the cursor to that line number. The compiler tries to show you what the error is by underlining the offending keystrokes in red.

  3. If the project fails to compile, then the errors discovered by the compiler will be listed at the bottom of the IDE screen. When all errors have been corrected (when the build is successful), the successful build will be announced at the lower-left corner of the IDE screen. Then it is time to run the project. To run it, you open the Debug main menu item and then click Start. Note that the shortcut for Start is F5.

    When the project compiles and runs, the main window should look like Figure 3-10.

    Image from book
    Figure 3-10: Executing Brave New World

    As soon as the user clicks on the Push Me Now button, the window changes to:

    Image from book
    Figure 3-11: Brave New World project after clicking the Push Me Now button

  4. To exit this project, the user must click on the X at the top right of the window.

The complete listing of an earlier version of Brave New World is shown below. Your source code should look a lot like this:

Brave New World (TN)

Form1.cs:
TN001:        using System;
TN002:        using System.Collections.Generic;
TN003:        using System.ComponentModel;
TN004:        using System.Data;
TN005:        using System.Drawing;
TN006:        using System.Text;
TN007:        using System.Windows.Forms;

TN010:        namespace Brave_New_World
TN011:        {
TN012:          public partial class Form1: Form
TN013:          {
TN014:            public Form1()
TN015:            {
TN016:              InitializeComponent();
TN017:            }
//-----------------------------------------------------------------------------/
TN021:            private void button1_Click(object sender, EventArgs e)
TN022:            {
TN023:              label2.Text = "Row, row, row your boat.";
TN024:            }
TN025:          }
TN026:        }
//=============================================================================/
Form1.Designer.cs:

TN040:        namespace Brave_New_World
TN041:        {
TN042:          partial class Form1
TN043:          {
                  // Required designer variable.
TN044:            private System.ComponentModel.IContainer components = null;
                  // Clean up any resources being used.
                  // <param name="disposing">true if managed resources should be disposed;
                      otherwise, false.</param>
TN045:            protected override void Dispose(bool disposing)
TN046:            {
TN047:              if (disposing && (components != null))
TN048:              {
TN049:                components.Dispose();
TN050:              }
TN051:              base.Dispose(disposing);
TN052:            }

TN053:            #region Windows Form Designer generated code
                  // Required method for Designer support - do not modify
TN054:            private void InitializeComponent()
TN055:            {
TN056:              this.label1 = new System.Windows.Forms.Label();
TN057:              this.button1 = new System.Windows.Forms.Button();
TN058:              this.label2 = new System.Windows.Forms.Label();
TN059:              this.SuspendLayout();
                    //
                    // label1
                    //
TN060:              this.label1.AutoSize = true;
TN061:              this.label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 10F,
                      System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point,
                      ((byte)(0)));
TN062:              this.label1.Location = new System.Drawing.Point(40, 34);
TN063:              this.label1.Name = "label1";
TN064:              this.label1.Size = new System.Drawing.Size(210, 16);
TN065:              this.label1.TabIndex = 0;
TN066:              this.label1.Text = "Welcome to this Brave New World!";
                    //
                    // button1
                    //
TN067:              this.button1.Location = new System.Drawing.Point(140, 74);
TN068:              this.button1.Name = "button1";
TN069:              this.button1.Size = new System.Drawing.Size(110, 25);
TN070:              this.button1.TabIndex = 1;
TN071:              this.button1.Text = "Push Me Now";
TN072:              this.button1.Click += new System.EventHandler(this.button1_Click);
                    //
                    // label2
                    //
TN073:              this.label2.AutoSize = true;
TN074:              this.label2.Font = new System.Drawing.Font("Microsoft Sans Serif", 10F,
                      System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point,
                      ((byte)(0)));
TN075:              this.label2.Location = new System.Drawing.Point(114, 125);
TN076:              this.label2.Name = "label2";
TN077:              this.label2.Size = new System.Drawing.Size(164, 16);
TN078:              this.label2.TabIndex = 2;
TN079:              this.label2.Text = "Good Grief, Charlie Brown!";
                    //
                    // Form1
                    //
TN080:              this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
TN081:              this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
TN082:              this.ClientSize = new System.Drawing.Size(392, 166);
TN083:              this.Controls.Add(this.label2);
TN084:              this.Controls.Add(this.button1);
TN085:              this.Controls.Add(this.label1);
TN086:              this.Name = "Form1";
TN087:              this.Text = "Form1";
TN088:              this.ResumeLayout(false);
TN089:              this.PerformLayout();
TN090:            }

TN091:            #endregion

TN092:            private System.Windows.Forms.Label label1;
TN093:            private System.Windows.Forms.Button button1;
TN094:            private System.Windows.Forms.Label label2;
TN095:          }
TN096:        }

Placing Windows in the Center of the Screen

The Brave New World project output is probably located at the top left of your PC screen. One of the features that was added to Visual Studio C# programming by the version 1.0, 1.1, and 2.0 releases is the ability to center every output window automatically. To do this, follow these steps:

  1. Click on the Brave New World Form1 in the IDE screen (which puts the window’s properties in the Properties window), and scroll down to the Layout group of properties.

  2. Highlight Start Position. As soon as you do this, a tiny down arrow appears at the right edge of the right column. Click on that down arrow to produce five different start positions for the window: Manual, Center Screen, Windows Default Location, Windows Default Bonds, and Center Parent. The default is Windows Default Location, which is the location that Brave New World presently has.

  3. Click on Center Screen, then recompile and rerun the project. The main window will now open in the center of the PC screen.

Creating Child Windows

There is one other important IDE function that we have not demonstrated so far: creating additional windows in the project (called child windows). If you decide to add other windows to this project, you would do so by clicking on the Project main menu item. As soon as you click on the Add Windows Form submenu item, the IDE creates another window (and names it “Form2”). Form1 is always the main window, and all child windows follow. Later we will demonstrate a multi-window project that uses the Project main menu item to create multiple windows.

This ends the demonstration of how to create a new project in the version 2.0 Visual C# compiler. Next, we show how to bring an already-existing project into the IDE.


Bringing an Existing Project into the IDE for Further Work

When you are in the IDE screen and you click on the File entry in the main menu, you are offered the choice of creating a new project or loading an existing project. To load an existing project, follow these steps:

  1. Click on File and choose Open in the submenu list.

  2. You are given three choices: Project Solution, File, and Convert. If the project you are loading is already in version 2.0 (MSBuild) format, click on Project Solution.

There is no present use for the File option. But if the project you are attempting to load comes from an earlier version of Visual Studio C#, select Convert. As it turns out, if you select the Project Solution option and the project folder is not in MSBuild format (a C# version 1.0 or 1.1 format), then the IDE will perform the conversion to the current configuration anyway.

Project Folder Conversion Sequence (from v1.0 or 1.1 Projects)

If this project is already in version 2.0 MSBuild format, the project opens in the IDE. However, if this is a conversion from an earlier project folder format, then the window in Figure 3-12 will appear.

Image from book
Figure 3-12: Visual Studio Conversion Wizard

  1. To move directly to completion (Figure 3-15), click on Finish. When you click on the Next > button to proceed, the window in Figure 3-13 appears.

    Image from book
    Figure 3-13: Choosing backup files

  2. If you choose Yes, a backup file will be created. If you choose No, a backup is not created.

    Click Finish to move directly to Figure 3-15, or click Next > to continue with the conversion (see Figure 3-14).

    Image from book
    Figure 3-14: Ready to Convert window

  3. After reviewing the summary information, click Finish to finish the conversion and proceed with the new MSBuild folder format. The window in Figure 3-15 appears.

    Image from book
    Figure 3-15: Conversion Complete window

You will find that the files placed in this upgraded version of an old C# project differ slightly from those files that are created by a totally new project.

Once the conversion is complete, the IDE will open with the upgraded project. You can build and run this upgraded project in the same manner that any other C# project is built and run.

Confusing Filenames

You will soon discover that there are three important files that describe a form (window) in Visual Studio C# version 2.0:

  • FormX.Designer.cs

  • FormX.cs

  • FormX.cs[Design]

These can be confusing to work with as the names are so similar. Be careful when using these files.

When you ask to see the FormX.cs[Design] file, you are shown a graphical representation of a window (the window template). When you ask to see the FormX.cs file, source code is presented in the IDE.

The FormX.Designer.cs file, which is totally computer generated (a long list of the controls in the project), can only be seen if you choose File from the main menu followed by Open and then File. Then double-click on the FormX.Designer.cs file you want to see. You can also view this file in WordPad since the file is a text file.

Normally, it is not necessary that you view the FormX.Designer.cs file. But there is one notable exception to this rule. In Chapter 5, “Passing Information between Windows,” we will show you how to change a private class object into a public class object so child windows can write information to that public class object in the main window (and vice versa). It is not a complicated procedure, but we demonstrate the process so you can use it in the future.

All controls are initially created as private class objects by the IDE. The change process (private to public or public to private) is also an automated process because the programmer cannot manually perform the task without seriously damaging the bookkeeping processes within a project. We will explain this fully in Chapter 5.

This completes the overview of the Microsoft IDE.


Chapter 4: Cloning an Existing Project

If you were creating a series of similar programs, it would seem foolish to create each succeeding program (once the first program is written) from scratch. It’s much simpler to clone the first program under a different name and make the changes to the cloned program after separating the clone from the original.

We will accomplish this cloning task in this chapter.

Cloning

For demonstration purposes the original program is one that was created for you and is located in Visual Studio 2005\ Projects\ DemosSourceCode\Thumbnail1. Its name is Thumbnail1. To perform the clone operation, you will create a second project named Thumbnail2 in the IDE. For each form in the original project, you create a new form in the second project. Each form (including the main window form) has its own FormX.cs source file and FormX.Designer.cs window template file.

Once the basic cloned project has been created by the programmer, the only tasks that remain are to transfer selected files from the original project to the new project (overwriting the files in the cloned project with files from the original project) and rename the namespace in the FormX.cs and FormX.Designer.cs files (since they contain the namespace of the old, original project inside their files).

The tasks that follow are required to clone the Thumbnail1 project into a Thumbnail2 configuration. Thumbnail2 can be placed anywhere under the Projects directory in Visual Studio 2005, so we have elected to place it in C:\Documents and Settings\Owner\My Documents\Visual Studio 2005\Projects\Cloned Projects so it can be immediately identified as a cloned project.

Task 1: Create New Project

Open the IDE and create a new project using the name Thumbnail2. Place the new project in C:\Documents and Settings\Owner\My Documents\Visual Studio 2005\Projects\Cloned Projects.

The Cloned Projects subdirectory does not exist at this time, but the IDE will create it for you. As soon as the project opens in the IDE you must manually create the same number of forms as the original project contains. In this case, Thumbnail1 only has one form (the main window), but if your original project had three forms you would do the following:

  1. After the new project (Thumbnail2) has been created and the IDE screen is presented, click on Project on the main menu. Click Add Windows Form, and the window in Figure 4-1 appears.

    Image from book
    Figure 4-1: Add New Item window

  2. Since the Visual C# Project Items category and the Windows Form template have been preselected by the compiler, all you have to do is click on the Add button at the lower right of the window to create a new form (Form2.cs in this case).

  3. The new Form2.cs and Form2.Designer.cs are created immediately. If you need a Form3, then you must click on the Project menu item again and click on Add Windows Form again. The number of forms in the new project must match the number of forms in the old project!

  4. As soon as this is done, move to File and Save All to save all elements of the new project. Then exit the project.

Task 2: Copy and Paste Files

Now you need to copy files from the old project and overwrite their counterparts in the new project. To do this, follow these steps:

  1. Move to My Documents\Visual Studio 2005\Projects\Cloned Projects.

  2. Open the Thumbnail2 subdirectory and click on the Thumbnail2 subdirectory that appears within the first Thumbnail2 directory. It is here, in this second Thumbnail2 subdirectory, that all file transfers will occur.

  3. Next, move to My Documents\Visual Studio 2005\Projects\ DemosSourceCode. Project Thumbnail1 is located there.

  4. Click on Thumbnail1, which displays a second Thumbnail1 subdirectory (and the Thumbnail1.sln file also). Click on the Thumbnail1 subdirectory. You now have two subdirectories open, so we suggest that you decrease the width of each and place them side by side for a series of file transfers. We always place the new project on the left side and the old project on the right side. At this time your screen should look like Figure 4-2.

    Image from book
    Figure 4-2: Opening two subdirectories

    If, for example, your original project had three windows (three forms), then the list shown above would include four more files (two for form2 and two for form3).

  5. Copy each FormX.cs file and each FormX.Designer.cs file from the right list and paste them onto the left list. In each case, the operating system will ask you if you wish to overwrite the old file; answer Yes.

You are almost finished with the cloning process. You must now make a minor change inside each FormX.cs and FormX.Designer.cs file that has been transferred from Thumbnail1 to Thumbnail2, using WordPad.

Task 3: Rename, Compile, and Run Clone of Original Project

Bring each of the Thumbnail2 FormX.cs and FormX.Designer.cs files into WordPad and change the namespace at the top of each file from Thumbnail1 to Thumbnail2. When these changes are complete, the cloning process is complete!

To verify that all is well, re-enter the compiler and bring up the Thumbnail2 project in the IDE. Compile it and run it. If you are an astute observer, you will note that the name Thumbnail1 remains in the title (at the top of the window), and you must click on the window template of Form1 to change the title from Thumbnail1 to Thumbnail2 within the Properties window.

Be aware that sometimes there are glitches in these file overwrites. The first time you attempt to compile the new cloned project you may see an error listing at the bottom of the IDE screen showing some sort of syntax error in either the FormX.cs or FormX.Designer.cs files. Double-click on that syntax error item to see the error. For some reason, it is fairly common for one or more of the semicolons in these files to be replaced with single quotes. You must fix the errors to proceed.

1. | Why did we go through this process of creating a second project (Thumbnail2) with the same number of forms to create this clone? Couldn’t we just copy all of project Thumbnail1, paste it in the Cloned Projects folder, and be done with it? | Image from book
2. | What if I add a form to a project and then find out that I don’t need it. Can I delete the window? | Image from book

Answers

1.

The reason this simplified cloning process won’t work is this: Thumbnail1 contains file paths in the supporting files that are okay for Thumbnail1 but are wrong for Thumbnail2. So the reason we create a copy of the project before transferring files is to set the proper file paths in the new (cloned) project.

IMPORTANT

This cloning business also works when a project refuses to open properly in the IDE. If a project will not open, then exit the IDE and move to the Projects folder in Visual Studio 2005. Rename this folder as an “old” folder. For example, rename “DragonSlayer” to “DragonSlayer(old).” Then return to the IDE and open a new project named “DragonSlayer.” Be sure to create the same number of forms (windows) in this new project as there are in the old project. Save All and exit the new project. Now set up two screens. The one on the right should list all the files in “DragonSlayer(old),” and the one on the left should list all the files in “DragonSlayer.”

Copy and paste the same subdirectories and files that we listed above. In this case you will not have to open each FormX.cs and FormX.Designer.cs file to change its namespace because the original files already have the right namespace.

Enter the IDE, open project DragonSlayer, and immediately compile it and run. If all goes well, then the problem with the old project not opening in the IDE is solved. Once this is done, destroy folder DragonSlayer(old).

2.

No, there is no provision to delete a form once it has been created in a project. But leaving the unused form in the project has no adverse affect on the project.


How Thumbnail1 (or Thumbnail2) Works

The main window of Thumbnail2 is shown in Figure 4-3:

Image from book
Figure 4-3: Thumbnail2 main window

The data that the user enters in the Answer text box is checked after every keystroke, using lines TH016-TH028 in the Thumbnail1 listing. If the number entered is not a digit (0-9), the MessageBox in line TH025 reports the error to the user, and the user is expected to fix the data entry problem.

As soon as the user clicks on the Is my answer correct? button, the focus has left the text box, so lines TH040-TH043 execute. The number the user entered in the text box is moved to a permanent location (since the text box data will disappear in a few milliseconds) in line TH042. An integer variable StIvesNumber has been declared in line TH013 (a variable global to the project) to hold the user’s answer until it can be checked later.

The action moves to lines TH050-TH054 to check the user’s answer. Since the only correct answer is 1, the code sets No into label9 if the answer is any number other than 1. If the user has entered 1, the text Yes is set into label9 for display.

To end the game the user clicks on the Quit button or on the X at the top right of the main window.

1.

What happens if you leave out the return statement on line TH026?

Answers

1.

This project works the same way, with or without the return statement. To try this out, comment out the line by placing two forward slashes just to the left of the return statement (while the project is loaded into the IDE). This tells the compiler that this is a comment line, not an executable statement.

Thumbnail1 (TH)

Form1.cs
TH001:        using System;
TH002:        using System.Collections.Generic;
TH003:        using System.ComponentModel;
TH004:        using System.Data;
TH005:        using System.Drawing;
TH006:        using System.Windows.Forms;

TH009:        namespace Thumbnail1
TH010:        {
TH011:          partial class Form1 : Form
TH012:          {
TH013:            public int StIvesNumber;

TH014:            public Form1()
TH015:            {InitializeComponent();}
//-----------------------------------------------------------------------------------------//
TH016:            private void textBox1_TextChanged(object sender, EventArgs e)
TH017:            { // Check that each character entered is a digit between 0 and 9.
TH018:              int Stringlength = textBox1.Text.Length;
TH019:              for (int jj = 0; jj < Stringlength; jj++)
TH020:              {
TH021:                if (Char.IsDigit(textBox1.Text[jj]))
TH022:                  { }
TH023:                else
TH024:                {
TH025:                  MessageBox.Show("Not a digit 0 - 9. Try Again!");
TH026:                  // return;
TH027:                }
TH028:              }
TH029:            }
//-----------------------------------------------------------------------------------------//
TH040:            private void textBox1_Leave(object sender, EventArgs e)
TH041:            { // Leaving textBox_1. Place data in a more permanent location.
TH042:              StIvesNumber = Convert.ToInt32(textBox1.Text);
TH043:            }
//-----------------------------------------------------------------------------------------//
TH050:            private void button1_Click(object sender, EventArgs e)
TH051:            { // Is my answer correct?
TH052:              if (StIvesNumber == 1) label9.Text = "Yes";
TH053:              else label9.Text = "No";
TH054:            }
//-----------------------------------------------------------------------------------------//
TH060:            private void button2_Click(object sender, EventArgs e)
TH061:            { // Quit this program.
TH062:              Close();
TH063:            }
TH064:          }
TH065:        }
//=========================================================================================//

Form1.Designer.cs
TH100:        namespace Thumbnail1
TH101:        {
TH102:          partial class Form1
TH103:          {

                  // Required designer variable.
TH104:            private System.ComponentModel.IContainer components = null;

                  // Clean up any resources being used.
TH105:            protected override void Dispose(bool disposing)
TH106:            {
TH107:              if (disposing && (components != null)) components.Dispose();
TH108:              base.Dispose(disposing);
TH109:            }

TH110:            #region Windows Form Designer generated code
                  // Required method for Designer support
TH111:            private void InitializeComponent()
TH112:            {
TH113:              this.label1 = new System.Windows.Forms.Label();
TH114:              this.label2 = new System.Windows.Forms.Label();
TH115:              this.label3 = new System.Windows.Forms.Label();
TH116:              this.label4 = new System.Windows.Forms.Label();
TH117:              this.label5 = new System.Windows.Forms.Label();
TH118:              this.label6 = new System.Windows.Forms.Label();
TH119:              this.label7 = new System.Windows.Forms.Label();
TH120:              this.label8 = new System.Windows.Forms.Label();
TH121:              this.textBox1 = new System.Windows.Forms.TextBox();
TH122:              this.button1 = new System.Windows.Forms.Button();
TH123:              this.label9 = new System.Windows.Forms.Label();
TH124:              this.button2 = new System.Windows.Forms.Button();
TH125:              this.SuspendLayout();
                    //
                    // label1
                    //
TH126:              this.label1.AutoSize = true;
TH127:              this.label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 10F,
                      System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point,
                      ((byte)(0)));
TH128:              this.label1.Location = new System.Drawing.Point(80, 18);
TH129:              this.label1.Margin = new System.Windows.Forms.Padding(3, 3, 3, 2);
TH130:              this.label1.Name = "label1";
TH131:              this.label1.Size = new System.Drawing.Size(177, 17);
TH132:              this.label1.TabIndex = 0;
TH133:              this.label1.Text = "As I was going to Saint Ives,";
                    //
                    // label2
                    //
TH134:              this.label2.AutoSize = true;
TH135:              this.label2.Font = new System.Drawing.Font("Microsoft Sans Serif", 10F,
                      System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point,
                      ((byte)(0)));
TH136:              this.label2.Location = new System.Drawing.Point(80, 36);
TH137:              this.label2.Margin = new System.Windows.Forms.Padding(3, 1, 3, 1);
TH138:              this.label2.Name = "label2";
TH139:              this.label2.Size = new System.Drawing.Size(187, 17);
TH140:              this.label2.TabIndex = 1;
TH141:              this.label2.Text = "I met a man with seven wives.";
                    //
                    // label3
                    //
TH142:              this.label3.AutoSize = true;
TH143:              this.label3.Font = new System.Drawing.Font("Microsoft Sans Serif", 10F,
                      System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point,
                      ((byte)(0)));
TH144:              this.label3.Location = new System.Drawing.Point(80, 54);
TH145:              this.label3.Margin = new System.Windows.Forms.Padding(3, 0, 3, 1);
TH146:              this.label3.Name = "label3";
TH147:              this.label3.Size = new System.Drawing.Size(178, 17);
TH148:              this.label3.TabIndex = 2;
TH149:              this.label3.Text = "Every wife had seven sacks,";
                    //
                    // label4
                    //
TH150:              this.label4.AutoSize = true;
TH151:              this.label4.Font = new System.Drawing.Font("Microsoft Sans Serif", 10F,
                      System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point,
                      ((byte)(0)));
TH152:              this.label4.Location = new System.Drawing.Point(80, 72);
TH153:              this.label4.Margin = new System.Windows.Forms.Padding(3, 0, 3, 1);
TH154:              this.label4.Name = "label4";
TH155:              this.label4.Size = new System.Drawing.Size(171, 17);
TH156:              this.label4.TabIndex = 3;
TH157:              this.label4.Text = "Every sack had seven cats,";
                    //
                    // label5
                    //
TH158:              this.label5.AutoSize = true;
TH159:              this.label5.Font = new System.Drawing.Font("Microsoft Sans Serif", 10F,
                      System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point,
                      ((byte)(0)));
TH160:              this.label5.Location = new System.Drawing.Point(80, 90);
TH161:              this.label5.Margin = new System.Windows.Forms.Padding(3, 0, 3, 1);
TH162:              this.label5.Name = "label5";
TH163:              this.label5.Size = new System.Drawing.Size(157, 17);
TH164:              this.label5.TabIndex = 4;
TH165:              this.label5.Text = "Every cat had seven kits,";
                    //
                    // label6
                    //
TH167:              this.label6.AutoSize = true;
TH168:              this.label6.Font = new System.Drawing.Font("Microsoft Sans Serif", 10F,
                      System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point,
                      ((byte)(0)));
TH169:              this.label6.Location = new System.Drawing.Point(80, 108);
TH170:              this.label6.Margin = new System.Windows.Forms.Padding(3, 0, 3, 1);
TH171:              this.label6.Name = "label6";
TH172:              this.label6.Size = new System.Drawing.Size(175, 17);
TH173:              this.label6.TabIndex = 5;
TH174:              this.label6.Text = "Kits, cats, sacks, and wives,";
                    //
                    // label7
                    //
TH175:              this.label7.AutoSize = true;
TH176:              this.label7.Font = new System.Drawing.Font("Microsoft Sans Serif", 10F,
                      System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point,
                      ((byte)(0)));
TH177:              this.label7.Location = new System.Drawing.Point(80, 126);
TH178:              this.label7.Margin = new System.Windows.Forms.Padding(3, 0, 3, 3);
TH179:              this.label7.Name = "label7";
TH180:              this.label7.Size = new System.Drawing.Size(227, 17);
TH181:              this.label7.TabIndex = 6;
TH182:              this.label7.Text = "How many were going to Saint Ives?";
                    //
                    // label8
                    //
TH183:              this.label8.AutoSize = true;
TH184:              this.label8.Font = new System.Drawing.Font("Microsoft Sans Serif", 10F,
                      System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point,
                      ((byte)(0)));
TH185:              this.label8.Location = new System.Drawing.Point(119, 160);
TH186:              this.label8.Name = "label8";
TH187:              this.label8.Size = new System.Drawing.Size(72, 17);
TH188:              this.label8.TabIndex = 7;
TH189:              this.label8.Text = "Answer -->";
                    //
                    // textBox1
                    //
TH190:              this.textBox1.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
TH191:              this.textBox1.Font = new System.Drawing.Font("Microsoft Sans Serif", 10F,
                      System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point,
                      ((byte)(0)));
TH192:              this.textBox1.Location = new System.Drawing.Point(185, 158);
TH193:              this.textBox1.Name = "textBox1";
TH194:              this.textBox1.Size = new System.Drawing.Size(37, 21);
TH195:              this.textBox1.TabIndex = 8;
TH196:              this.textBox1.Leave += new System.EventHandler(this.textBox1_Leave);
TH197:              this.textBox1.TextChanged += new
                      System.EventHandler(this.textBox1_TextChanged);
                    //
                    // button1
                    //
TH198:              this.button1.Font = new System.Drawing.Font("Microsoft Sans Serif", 10F,
                      System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point,
                      ((byte)(0)));
TH199:              this.button1.Location = new System.Drawing.Point(119, 200);
TH200:              this.button1.Name = "button1";
TH201:              this.button1.Size = new System.Drawing.Size(176, 23);
TH202:              this.button1.TabIndex = 9;
TH203:              this.button1.Text = "Is my answer correct?";
TH204:              this.button1.Click += new System.EventHandler(this.button1_Click);
                    //
                    // label9
                    //
TH205:              this.label9.AutoSize = true;
TH206:              this.label9.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F,
                      System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point,
                      ((byte)(0)));
TH207:              this.label9.Location = new System.Drawing.Point(187, 237);
TH208:              this.label9.Name = "label9";
TH209:              this.label9.Size = new System.Drawing.Size(0, 0);
TH210:              this.label9.TabIndex = 10;
                    //
                    // button2
                    //
TH211:              this.button2.Location = new System.Drawing.Point(299, 269);
TH212:              this.button2.Name = "button2";
TH213:              this.button2.TabIndex = 11;
TH214:              this.button2.Text = "Quit";
TH215:              this.button2.Click += new System.EventHandler(this.button2_Click);
                    //
                    // Form1
                    //
TH216:              this.AutoScaleDimensions = new System.Drawing.SizeF(5F, 13F);
TH217:              this.ClientSize = new System.Drawing.Size(392, 306);
TH218:              this.Controls.Add(this.button2);
TH219:              this.Controls.Add(this.label9);
TH220:              this.Controls.Add(this.button1);
TH221:              this.Controls.Add(this.textBox1);
TH222:              this.Controls.Add(this.label8);
TH223:              this.Controls.Add(this.label7);
TH224:              this.Controls.Add(this.label6);
TH225:              this.Controls.Add(this.label5);
TH226:              this.Controls.Add(this.label4);
TH227:              this.Controls.Add(this.label3);
TH228:              this.Controls.Add(this.label2);
TH229:              this.Controls.Add(this.label1);
TH230:              this.Name = "Form1";
TH231:              this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
TH232:              this.Text = "Thumbnail1";
TH233:              this.ResumeLayout(false);
TH234:              this.PerformLayout();
TH235:            }

TH236:            #endregion

TH237:            private System.Windows.Forms.Label label1;
TH238:            private System.Windows.Forms.Label label2;
TH239:            private System.Windows.Forms.Label label3;
TH240:            private System.Windows.Forms.Label label4;
TH241:            private System.Windows.Forms.Label label5'
TH242:            private System.Windows.Forms.Label label6;
TH243:            private System.Windows.Forms.Label label7;
TH244:            private System.Windows.Forms.Label label8;
TH245:            private System.Windows.Forms.TextBox textBox1;
TH246:            private System.Windows.Forms.Button button1;
TH247:            private System.Windows.Forms.Label label9;
TH248:            private System.Windows.Forms.Button button2;
TH249:           }
TH250:          }


Chapter 5: Passing Information between Windows

Overview

When the battle lines were drawn in the 1980s between the groups who wanted computer programming to remain more of an art than a science versus those who wanted hard-and-fast mandatory programming standards, the proclamation of both sides was that the software products delivered to the customers should be as useful and inexpensive as possible. Both groups thought they could achieve their goals in totally different ways.

The mandatory standards group had a lot more ammunition to support its claims that the artistic groups were doing far more harm than they were good for the fledgling software industry. For example, there was the case in Springfield, Ohio, where one brilliant IBM assembler language specialist managed to create the software for most of the banks in and around his hometown, and his techniques for placing the assembler code in the source code listing were so mystifying that no one else could figure out what he had done. His deftness at producing spaghetti code was unsurpassed. One summer he announced that he was fed up with the paltry pay he was receiving from the banks and left for a long vacation.

In a month he brought all the banks that used his assembler software to their knees. He had hard-wired into the code every interest rate used by the banks, every fee they charged, and the format of every form the banks generated on their computers. The argument between the banks and the programmer could not be settled in the courts because there were no published standards on how such software should be prepared and documented so other programmers could maintain the code when the originator retired or left or died.

The only reason that assembler code thrived for a time in the 1970s and 1980s was because the region allocated in all digital computers to house both the computational program and all its data was only 64 K (the number of memory locations that could be addressed with 16 wires). Assembler code was significantly smaller than the code produced by a general software language like FORTRAN, so it was greatly desired by corporations that needed large amounts of computing region to support their operations.

But assembler code was extremely expensive to maintain because it was often poorly documented. So management made the move toward programming exclusively in COBOL or FORTRAN, and wrote minimum documentation requirements into software contracts. For example, if a program became too large to fit into a 64 K region, then it was cut up into pieces and placed in multiple regions. When the computation in one region was complete, the intermediate output data was written to a file, and that file became the input data for the next region’s work. This was not easy to do, but resourceful programmers made it work. For example, most payroll programs consisted of two parts that were loaded in sequence: Part one computed each employee’s pay and wrote the final numbers to a file, and part two picked up the file and printed checks. In January of each year a third part was loaded — the part that issued W2 and 1099 IRS forms to the employees.

To overcome the spaghetti code tendencies of some programmers, computer science schools began to teach structured programming, a concept where there was only one way into a block of code (at the top) but possibly more than one way out of a block of code. Once a block of code was exited, the only way back in was at the top.

The concept of a local variable versus a global variable was brought to the fore at this time. A local variable was one that was set into one structure to support only that structure. A global variable was declared at the top of a FORTRAN or COBOL program and, because of its location and unique declaration in the source code, it was available to all structures within the region. But what if a local variable happened to be given the same name as a global variable at the top of the source code list? Could the source code sort out whether the variable was local in one part of the region and global in another part?

No, not really. And it was this ambiguity that terrorized large software programs for several generations. At Wright-Patterson Air Force Base there was great concern in one of the flight simulation laboratories because one of its simulations of a Russian surface-to-air missile always broke off the engagement of the target when the missile came within 100 meters of the target and gyrated wildly away from the point where the kill was supposed to occur. What was the problem? Finally, one programmer ended the mystery when she changed very slightly the name of one FORTRAN variable in the “final assault” package that commanded the last 100 meters of missile flight. Suddenly the simulation worked exactly as it should.

With rampant error generation in large software programs, management made its move to compartmentalize each block of code and tag every variable inside with either a “public” or a “private” label, and the compiler writers promised to keep everything straight inside with a new generation of compilers. Most computer programmers were furious! There wasn’t even a hint of art left in the programming business any more — it was all predetermined, rote source code sequences that everyone had to use. As one programmer explained, “It’s like every structure inside the code was outfitted with an impenetrable Berlin Wall and there was no way to defeat it.”

The final blow came with the advent of object-oriented programming. The object-oriented architects wanted all computer sequences along with all the data presented to or removed from those sequences to be located in one place. Around that place the architects built a wall, and if someone wanted access to a variable or procedure inside that wall, she had to ask permission to do so.

Was this supreme acrimony or what? Well, as it has turned out, all the baby steps toward standardization of the computer software industry have indeed saved us from ourselves, and today you have reasonable assurance of reaching your stated goals for large software programs. They might even work! With all this structure built into modern compilers, there is also reasonable assurance that, once a compiler agrees that the form of the code you are writing is acceptable, the code may function properly after it is compiled and run. This was never true in the old FORTRAN and COBOL days.

But the walls that were built around each structure make it more difficult for the individual programmer to access variables or procedures within another structure (one written by someone else). Some compiler architects, like Borland for example, made it relatively easy to gain access from the outside to a variable located inside a form despite the walls between the structures. For example, if a variable named MeToo in Form1 needed the contents of a variable named BigRed that was calculated in Form2, then this simple statement did the trick:

MeToo = Form2 -> BigRed;

But allowing this transfer to occur was a violation of the strict object-oriented substratum agreed to around the world. The architects of Microsoft Visual Studio C# would not allow this method to be used to pass information between Form2 and Form1. Instead, they configured the Visual Studio C# compiler such that the programmer must plan ahead and pass a handle to an adjacent form at the time that adjacent form is created — a handle identifying the form that would receive data later. This passing a handle between forms meets all the requirements imposed on structured programming, object-oriented design, and so forth.


A Demonstration: Passing Information between Windows

We will now proceed with a demonstration of how to pass information between a main window and its surrounding child windows, using approved object-oriented procedures. The demonstration includes:

  • How to create a typical Visual Studio C# project (main window plus two child windows).

  • How the main window talks to the child windows, and how the child windows talk back to the main window (two-way communication).

  • How to implement one-way communication (main window to child) using arguments. This method has been in vogue for 40 years in the computer industry.

The name of this project is OverTheRiverAndThroughTheWoods.

Do you recall the story of Little Red Riding Hood? She went to visit her grandmother in the woods, but her grandmother was replaced by a big bad wolf pretending to be her grandmother. In our story today Little Red Riding Hood (LRRH) decides to visit her two grandmothers, both of whom live a short distance away from her home in Bel-Air. Because LRRH’s mother is concerned for her daughter’s safety, she has constructed communications between home (the main window) and each of the grandmother’s homes (first child window and second child window) so she and LRRH can send messages to each other.

Before LRRH departs home for Grandma #1’s house, she sits down at her computer and sends the lady a message announcing her departure. Then, when LRRH arrives at Grandma #1’s house she sends a message back home to her mother, announcing her arrival. This is an exercise in messaging.

  1. Open the Visual Studio C# IDE, and choose File on the main menu, followed by New and then Project. When the New Project window opens, name the project OverTheRiverAndThroughTheWoods and store the project at C:\Documents and Settings\Owner\ MyDocuments\Visual Studio 2005\Projects. Click on the OK button at the bottom-right edge of the window to create the new project. Highlight the window template so its properties will appear in the Properties window. Then scroll down to the Text entry and change the title bar from “Form1” to Over the River and Through the Woods.

    IMPORTANT

    You change the Text entry here, not the (Name) entry. The object’s name always remains the same.

  2. Scroll down to Layout, then Size, and set the form’s size to (550, 300).

  3. Immediately below the Size entry, click on the Start Position entry and pick Center Screen.

  4. Click on the Toolbox tab at the left edge of the IDE screen to display the Toolbox. In Toolbox, click once on Label (not LinkLabel), then move the mouse cursor over the window template and click again to deposit the label on the template. This is label1. Since the label is highlighted on the template, its properties appear in the Properties window.

  5. Set the label’s Location to (100, 8). You cannot choose a size for a label in v2.0; that is done automatically. Set its Text as Over the River and Through the Woods. Set the label’s Font Size to 14 and its Font Underline to true.

Note

The window template, like the Windows screen, uses coordinates with a (0,0) point at the top left of the screen. The coordinates increase as you move down or right on the screen. The unit of measurement is the pixel. The most-coarse screen coordinates are 800 pixels wide by 600 pixels high. Any label or control that you place on a Windows screen takes its position coordinates from the top left of the screen. If, however, you place a group box onto the template, any control you place inside the group box takes its coordinates from the top left of the group box. You will notice the change in coordinate systems as we proceed through OverTheRiverAndThroughTheWoods.

This is going to be a busy project. You will place six more labels on the template, two text boxes, one group box, and five buttons. The finished main window template will look something like this:

Image from book
Figure 5-1: OTRATTW window

Only in special cases does the order in which you place the labels/ controls on the template make a difference in the outcome. On this OTRATTW template we have a special case: The group box must be placed first, then the four labels (Moses:, label6, Juliette:, label7) must be placed on top of the group box.

  1. Create and place label2 (Send Note to Grandma Moses ->) at location (24, 52). The font size is 10 with no underline, and the text is Send Note to Grandma Moses ->.

  2. Create and place label3 (Send Note to Grandma Juliette ->) at location (24, 86). The font size is 10 with no underline, and the text is Send Note to Grandma Juliette ->.

  3. Create and place textBox1 at location (235, 50) with size (200, 23). The font size is 10 with no underline. No new text is required here.

  4. Create and place textBox2 at location (235, 84) with size (200, 23). The font size is 10 with no underline. No new text is required here.

  5. Create and place groupbox1 at location (28, 120) with size (488, 64). Its text is Messages From Our Grandmother:. The font size is 10 with no underline.

These next four labels must be placed on groupbox1.

  6. Create and place label4 (Moses:) on top of groupbox1 with location (16, 20). The text is Moses: and the font size is 10 with no underline.

  7. Create and place label5 (Juliette:) with location (16, 40). The text is Juliette: and the font size is 10 with no underline.

  8. Create and place label6 at location (75, 20). Font size is 10 with no underline.

  9. Create and place label7 at location (75, 40). Font size is 10 with no underline.

    Note

    We will erase the words “label6” and “label7” (fill them with blanks) now so the area inside groupbox1 will be empty except for the words “Moses:” and “Juliette:”. Highlight label6 and replace the label6 Text with blanks. Do the same thing for label7.

    Once you place blanks in a label, the template cannot show the programmer where the label is located, so we usually wait to blank out a label after all other work is complete on the form.

    IMPORTANT

    All controls and forms are initially created by the IDE as private class objects. Since we will be writing to label6 and label7 from remote child windows, label6 and label7 must be converted from private to public classes. The programmer cannot accomplish this task manually. (Changing “private” to “public” in the Form1.Designer.cs file doesn’t work, since the IDE changes them back to “private” immediately.)

    Image from book
    Changing a Private Class to a Public Class

    To change label6 and label7 from private to public, proceed as follows:

    1. Get label6 into the Properties window. If label6 had any characters in it, it would be easy to click on that label in the window template and highlight it (so its properties appear in the Properties window). But in this case label6 has no characters in it, so it cannot be located on the template! What do you do? Click on any object to open the Properties window. At the top of the window the name of the object appears, and to the right of that name is a drop-down down arrow (directly below the black X that closes the window). Click on the down arrow to reveal the list of all objects.

    2. Click on label6; its properties will appear in the Properties window. Scroll down to Design, click on Modifiers, and choose Public (it was originally set at Private). Now the child windows can write to label6 (after the proper handle has been given by the main window to the child windows).

    3. Repeat this process for label7.

    Image from book

  10. Create and place button1 at location (452, 50), with size (60, 22). The font size is 8.25 with no underline, and the text is Send.

  11. Create and place button2 at location (452, 84), with size (60, 22). The font size is 8.25 with no underline, and the text is Send.

  12. Create and place button3 at location (50, 195), with size (200, 23). The font size is 8.25 with no underline, and the text is Going to Grandma Moses’ House.

  13. Create and place button4 at location (300, 195), with size (200, 23). The font size is 8.25 with no underline, and the text is Going to Grandma Juliette’s House.

  14. Create and place button5 at location (208, 226), with size (125, 23). The font size is 8.25 with no underline, and the text is Quit this Project.

This completes the placement of all the labels/controls on the main window template.

Next we will connect some of the controls to event handlers that the IDE will create for us automatically.


Default Event Handlers vs. Non-Default Event Handlers

Every control shown in the Toolbox has a default event handler. For example, the default for a button is buttonX_Click (the procedure that is activated when the user clicks on the button). For the text box, the default is textBoxX_TextChanged, the procedure used to check the characters being entered into the text box by the user. To invoke the default event handler for any control, the programmer highlights the control on the window template and double-clicks it. The IDE places the procedure in the FormX.cs source code file.

If a non-default event handler is required, the programmer must highlight the control in the template, move to the Properties window, click on the Events icon, and scroll up or down to find the proper event. Once the proper event is located in the left column, the programmer double-clicks that item to create the procedure in the FormX.cs source code file.

Creating the Necessary Event Handlers

In this project we will create default events handlers only. As each event handler is created, we will immediately enter a comment within the source code in the FormX.cs file to indicate which handler is which. With so many handlers it is easy to get them mixed up.

Note

There are two ways to place comments in your Visual Studio C# code. Method one is used here: Enter two forward slashes; anything entered to the right of these slashes on the same line is ignored by the compiler. The compiler overlooks entries only on this line. The second method allows you to comment out multiple lines of code. At the start point you type /* and at the end point you type */. This is nothing new to C programmers; we have done this for decades.

The advantage of the /* -- */ sequence is that it can surround any // comments that you placed in the text earlier, and it does not damage any earlier comments you placed in the text.

None of the labels need event handlers. The two text boxes require event handlers only if we decide to do some text checking within each text box (similar to what was done in project Thumbnail1 or Thumbnail2). Since we are not going to check the text that the user types into the text boxes for correctness, the text boxes need no event handlers. However, the buttons definitely need default event handlers. We will create those handlers now.

1. | What if you create a handler and discover later that you don’t need it. How do you get rid of it?
2. | Once I am in the event handler list (the Events window), how do I get out of it?

Answers

1. | Highlight the control with the unwanted handler, move to the Properties window, click on the Events icon at the top of the window, find the unwanted handler in the list, and right-click on that entry. A pop-up menu appears. Choose the Reset subitem, and the entire handler goes away from the project’s hidden connections. If the programmer has not typed any text into the procedure that the event handler created in the FormX.cs source code file, that procedure will be eliminated. However, if the programmer has entered any text into the procedure, the automatic system cannot erase the procedure and the programmer must do so manually.
2. | Click on the Properties icon at the top of the window (just to the left of the bolt of lightning icon) to return to the Properties window.

  1. Highlight button1 on the template, double-click on it, and create a handler named button1_Click. Place the comment // Send Grandma Moses message. inside the source code (between the two curly brackets that define the procedure).

    After each of the following events, click on the F7 key to return the window template to the screen.

  2. Highlight button2, double-click on it, and create a default handler named button2_Click. Place the comment // Send Grandma Juliette message. inside the source code (between the curly brackets that define the procedure).

  3. Highlight button3, double-click on it, and create a default handler named button3_Click. Place the comment // Going to Grandma Moses’ house. inside the source code.

  4. Highlight button4, double-click on it, and create a default handler named button4_Click. Place the comment // Going to Grandma Juliette’s house. inside the source code.

  5. Highlight button5, double-click on it, and create a default handler named button5_Click. Place the comment // Quit this project. inside the source code.

All that remains in the main window (Form1) is the placement of selected code in some of the event handlers.

  1. Move to the event handler for button 1, button1_Click. When the user clicks on this button to send a message to Grandma Moses, all that this handler must do is transfer the message from the temporary location textBox1.Text to a permanent, persistent, global variable that is declared at the top of Form1 (the main window): strGrandmaMoses. If you do not transfer the data in textBox1.Text to strGrandmaMoses (which is a public, global variable in Form1), the data cannot be transferred to Form2 later.

  2. Add these two lines of code in the button1_Click event handler:

// Place this message into a public string, for transport to Form2.
strGrandmaMoses = textBox1.Text;

  3. Move to the event handler button2_Click and add these lines:

// Place this message into a public string, for transport to Form3.
strGrandmaJuliette = textBox2.Text;

  4. Move to the event handler button3_Click and enter this code:

// Create Form 2 (Grandma Moses' house).
// 'this' in the next line refers to the main window's 'handle'.
Form2 frm2 = new Form2(this);
frm2.ShowDialog();
frm2.Close();

  5. Move to the event handler button4_Click and enter this code:

// Create Form 3 (Grandma Juliette's house).
// 'this' in the next line refers to the main window's 'handle'.
Form3 frm3 = new Form3(this);
frm3.ShowDialog();
frm3.Close();

  6. Move to the event handler button5_Click and enter:

// Quit this project.
Close();

  7. Move to the top of the Form1.cs file, to where the classes are declared, and add two comment lines and two declarations:

partial class Form1 : Form                      (this line is already present)
{                                               (this line is already present)
  // These are two programmer-added items:      (add this line)
  public string strGrandmaMoses;                (add this line)
  public string strGrandmaJuliette;             (add this line)
  //                                            (add this line)
public Form1();                                 (this line is already present)
    IMPORTANT

    The statements above must appear precisely as shown (between the opening curly bracket and the Form1 line).

  8. Labels 6 and 7 have already been converted from private objects to public objects so the child windows can send information back to them.

This completes all code entries into Form1.

The next task is to create two new child windows (Form2 and Form3) in the project.


Creating Form2 and Form3

  1. On the main IDE window, click on the main menu item Project, then Add Windows Form. The Add New Item window appears and the new item, Form2.cs, appears in the Name text box. Click on the Open button at the bottom of the window to create Form2.

  2. Repeat this procedure to create Form3.

Figures 5-2 and 5-3 show what Form2 and Form3 should look like eventually (after you have added several controls to each one):

Image from book
Figure 5-2: Child window #1 (Form2)
Image from book
Figure 5-3: Child window #2 (Form3)

  1. Move to the Form2 window template and set its size to (400, 200). Sets its title bar text to Grandma Moses’ House. Set its Start Position to Center Screen.

  2. Create and place label1 (Message From Home:) at location (16, 16). The font size is 10 and the message is underlined. The text is Message From Home:.

  3. Create and place label2 at location (16, 40). The font size is 10. There is no new text.

  4. Create and place label3 at location (16, 72). The font size is 10 and the message is underlined. The text is Send Message to Home:.

  5. Create and place textBox1 at location (24, 94) with size (340, 23). The font size is 10. No new text is required here.

  6. Create and place button1 at location (137, 128), with size (125, 23). The font size is 8.25, and the text is Return To Home.

This completes all the labels/controls on Form2.

It is time to create the event handlers for Form2. The only control that requires a handler is button1, the Return To Home button. When the user clicks on this button we will place the text in textBox1 into a persistent location in Form1 (the main window).

However, Form2 requires that items be entered on the form before any control is actuated (as soon as the window is created), so we must create the general event handler that is a part of the entire form window. Highlight the window template, move to the Properties window, and click on the Events icon. Under Behavior you will find a handler named Load. Double-click on Load to create an event handler named Form2_Load. This completes all formation of event handlers.

Note

The easiest way to initialize data in a newly formed window is to place the event FormX_Load into its source code since this function is executed before the window opens on one’s PC. The Load event is located in the list of events in every window template.

Each of the two event handlers that have been created within Form2 require code:

  1. In Form2_Load place this code:

// Place the main window's message into 'label2'.
label2.Text = frm1Parent.strGrandmaMoses;

  2. In button1_Click place this code:

// Place the return message into persistent storage in Form1:
frm1Parent.label6.Text = textBox1.Text;
Close();

This completes all code entries into Form2.

One last item remains in Form2: informing Form2 that it should expect data from Form1 (and vice versa). This is accomplished at the top of the class declaration. You will add six lines of code and modify one:

partial class Form2 : Form                  (no change)
{                                           (no change)
  // This is a programmer-added item:       (add this line) ******
  public Form1 frm1Parent;                  (add this line) ******
  //                                        (add this line) ******
  // This next line is programmer-amended:  (add this line) ******
  public Form2(Form1 frm1)             (modify this line with an argument Form1 frm1) ******
  {                                         (no change)
    // This next line is programmer-added:  (add this line) ******
    frm1Parent = frm1;                      (add this line) ******
    InitializeComponent();                  (no change)

This completes the rework of Form2. At the end of this section we will discuss how the communication system between the main window and child windows works.

Repeat the process for Form3 (three labels, one text box, and one button), keeping in mind that Form3 is “Grandma Juliette’s House,” not “Grandma Moses’ House.”

public class Form3 : Form                   (no change)
{                                           (no change)
  // This is a programmer-added item:       (add this line) ******
  public Form1 frm1Parent;                  (add this line) ******
  //                                        (add this line) ******
  // This next line is programmer-amended:  (add this line) ******
  public Form3(Form1 frm1)             (modify this line with an argument Form1 frm1) ******
  {                                         (no change)
    // This next line is programmer-added:  (add this line) ******
    frm1Parent = frm1;                      (add this line) ******
    InitializeComponent();                  (no change)

The event handlers are the same, but there are minor changes in the code you must enter in the handlers:

1. In the Form3_Load event handler, place this code:

// Place the main window's message into 'label2'.
label2.Text = frm1Parent.strGrandmaJuliette;

2. In the button1_Click handler, place this code:

// Place the return message into persistent storage in Form1:
frm1Parent.label7.Text = textBox1.Text;
Close();

This completes all code entries into Form3.

Build and run this project. You should be able to write messages to the grandmas’ houses before you depart home (the main window) for their houses. (Don’t forget to send the messages by clicking on the Send button after you type text in the text boxes.) Once LRRH is at the grandmothers’ houses, you should be able to send messages back home.

If a problem persists, the complete, working project may be found in Projects\DemosSourceCode\OverTheRiverAndThroughTheWoods.


Two-Way Communication

These are the procedures used for two-way communication between a main window and a child window, using the code listing that follows:

  1. Main window

    1. OR011 and OR012: Declare public, global string variables strGrandmaMoses and strGrandmaJuliette so the data the user enters into the text boxes may be sent to Form2 and Form3 (private won’t work).

    2. OR251 and OR252: Convert any declarations of variables in main that must receive data from the child windows from private to public (label6 and label7).

    3. OR042: Create child window Form2 and send the main window handle named this to that child window so it can send data back to the main window later.

    4. OR052: Create child window Form3 and send the main window handle named this to that child window so it can send data back to the main window later.

  2. Child window (Grandma Moses’ House)

    1. OR311: Create a public Form1 variable within Form2 to receive the main window handle: public Form1 frm1Parent.

    2. OR312: Receive the Form1 handle (named frm1) as an argument in the Form2 class: Form1 frm1.

    3. OR314: Place the handle name into frm1Parent: frm1Parent = frm1.

    4. OR322: Transfer the message from the main window to label2 in the child window: label2.Text = frm1Parent.strGrandmaMoses.

    5. OR332: Transfer the message from the child window to a main window address: frm1Parent.label6.Text = textBox1.Text.

  3. Child window (Grandma Juliette’s House)

    1. OR481: Create a public Form1 variable in Form3 to receive the main window handle: public Form1 frm1Parent.

    2. OR482: Receive the Form1 handle (named frm1) as an argument in the Form3 class: Form1 frm1.

    3. OR484: Place the handle name into frm1Parent: frm1Parent = frm1.

    4. OR502: Transfer the message from the main window to label2 in the child window: label2.Text = frm1Parent.strGrandma-Juliette.

    5. OR512: Transfer the message from the child window to a main window address: frm1Parent.label7.Text = textBox1.Text.

1.

Are the two child windows able to “talk” to one another?

Answers

1.

No, the child windows can only pass information to the main window. It would be possible to construct a means of communication between the two, but the data would always have to be placed in a persistent location in main by one child window and retrieved by the other child window.

The complete OverTheRiverAndThroughTheWoods code is shown below:

OverTheRiverAndThroughTheWoods (OR) Listing

Form1.cs:
OR001:        using System;
OR002:        using System.Collections.Generic;
OR003:        using System.ComponentModel;
OR004:        using System.Windows.Forms;
OR005:        using System.Data;
OR006:        using System.Drawing;

OR007:        namespace OverTheRiverAndThroughTheWoods
OR008:        {
OR009:          partial class Form1 : Form
OR010:          {
                  // These are two programmer-added items:
OR011:            public string strGrandmaMoses;
OR012:            public string strGrandmaJuliette;

OR013:            public Form1()
OR014:            {
OR015:              InitializeComponent();
OR016:            }
//-----------------------------------------------------------------------------------------//
OR020:            private void button1_Click(object sender, EventArgs e)
OR021:            { // Send Grandma Moses message.
                    // Place this message into a public string, for transport to Form2.
OR022:              strGrandmaMoses = textBox1.Text;
OR023:            }
//-----------------------------------------------------------------------------------------//
OR030:            private void button2_Click(object sender, EventArgs e)
OR031:            { // Send Grandma Juliette message.
                    // Place this message into a public string, for transport to Form3.
OR032:              strGrandmaJuliette = textBox2.Text;
OR033:            }
//-----------------------------------------------------------------------------------------//
OR040:            private void button3_Click(object sender, EventArgs e)
OR041:            { // Going to Grandma Moses' house.
                    // Create Form2 (Grandma Moses' house).
                    // 'this' in the next line refers to the main window's 'handle'.
OR042:              Form2 frm2 = new Form2(this);
OR043:              frm2.ShowDialog();
OR044:              frm2.Close();
OR045:            }
//-----------------------------------------------------------------------------------------//
OR050:            private void button4_Click(object sender, EventArgs e)
OR051:            { // Going to Grandma Juliette's house.
                    // Create Form3 (Grandma Juliette's house).
                    // 'this' in the next line refers to the main window's 'handle'.
OR052:              Form3 frm3 = new Form3(this);
OR053:              frm3.ShowDialog();
OR054:              frm3.Close();
OR055:            }
//-----------------------------------------------------------------------------------------//
OR060:            private void button5_Click(object sender, EventArgs e)
OR061:            { // Quit this project.
OR062:              Close();
OR063:            }
OR064:          }
OR065:        }
//=========================================================================================//
Form1.Designer.cs:
OR080:        namespace OverTheRiverAndThroughTheWoods
OR081:        {
OR082:          partial class Form1
OR083:          {
                  // Required designer variable.
OR084:            private System.ComponentModel.IContainer components = null;
                  // Clean up any resources being used.
OR085:            protected override void Dispose(bool disposing)
OR086:            {
OR087:              if (disposing && (components != null))
OR088:              {
OR089:                components.Dispose();
OR090:              }
OR091:              base.Dispose(disposing);
OR092:            }

OR093:            #region Windows Form Designer generated code
                  // Required method for Designer support - do not modify
OR094:            private void InitializeComponent()
OR095:            {
OR096:              this.label1 = new System.Windows.Forms.Label();
OR097:              this.label2 = new System.Windows.Forms.Label();
OR098:              this.label3 = new System.Windows.Forms.Label();
OR099:              this.textBox1 = new System.Windows.Forms.TextBox();
OR100:              this.textBox2 = new System.Windows.Forms.TextBox();
OR101:              this.groupBox1 = new System.Windows.Forms.GroupBox();
OR102:              this.label7 = new System.Windows.Forms.Label();
OR103:              this.label6 = new System.Windows.Forms.Label();
OR104:              this.label5 = new System.Windows.Forms.Label();
OR105:              this.label4 = new System.Windows.Forms.Label();
OR106:              this.button1 = new System.Windows.Forms.Button();
OR107:              this.button2 = new System.Windows.Forms.Button();
OR108:              this.button3 = new System.Windows.Forms.Button();
OR109:              this.button4 = new System.Windows.Forms.Button();
OR110:              this.button5 = new System.Windows.Forms.Button();
OR111:              this.groupBox1.SuspendLayout();
OR112:              this.SuspendLayout();
                    //
                    // label1
                    //
OR113:              this.label1.AutoSize = true;
OR114:              this.label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 14F,
                       System.Drawing.FontStyle.Underline, System.Drawing.GraphicsUnit.Point,
                       ((byte)(0)));
OR115:              this.label1.Location = new System.Drawing.Point(100, 8);
OR116:              this.label1.Name = "label1";
OR117:              this.label1.Size = new System.Drawing.Size(341, 24);
OR118:              this.label1.TabIndex = 0;
OR119:              this.label1.Text = "Over the River and Through the Woods";
                    //
                    // label2
                    //
OR120:              this.label2.AutoSize = true;
OR121:              this.label2.Font = new System.Drawing.Font("Microsoft Sans Serif", 10F,
                       System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point,
                       ((byte)(0)));
OR122:              this.label2.Location = new System.Drawing.Point(24, 52);
OR123:              this.label2.Name = "label2";
OR124:              this.label2.Size = new System.Drawing.Size(206, 17);
OR125:              this.label2.TabIndex = 1;
OR126:              this.label2.Text = "Send Note to Grandma Moses ->";
                    //
                    // label3
                    //
OR127:              this.label3.AutoSize = true;
OR128:              this.label3.Font = new System.Drawing.Font("Microsoft Sans Serif", 10F,
                       System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point,
                       (byte)(0)));
OR129:              this.label3.Location = new System.Drawing.Point(24, 86);
OR130:              this.label3.Name = "label3";
OR131:              this.label3.Size = new System.Drawing.Size(209, 17);
OR132:              this.label3.TabIndex = 2;
OR133:              this.label3.Text = "Send Note to Grandma Juliette ->";
                    //
                    // textBox1
                    //
OR134:              this.textBox1.Font = new System.Drawing.Font("Microsoft Sans Serif", 10F,
                       System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point,
                       ((byte)(0)));
OR135:              this.textBox1.Location = new System.Drawing.Point(235, 50);
OR136:              this.textBox1.Name = "textBox1";
OR137:              this.textBox1.Size = new System.Drawing.Size(200, 23);
OR138:              this.textBox1.TabIndex = 3;
                    //
                    // textBox2
                    //
OR139:              this.textBox2.Font = new System.Drawing.Font("Microsoft Sans Serif", 10F,
                       System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point,
                       ((byte)(0)));
OR140:              this.textBox2.Location = new System.Drawing.Point(235, 84);
OR141:              this.textBox2.Name = "textBox2";
OR142:              this.textBox2.Size =new System.Drawing.Size(200,23);
                       this.textBox2.TabIndex = 4;
                    //
                    // groupBox1
                    //
OR143:              this.groupBox1.Controls.Add(this.label7);
OR144:              this.groupBox1.Controls.Add(this.label6);
OR145:              this.groupBox1.Controls.Add(this.label5);
OR146:              this.groupBox1.Controls.Add(this.label4);
OR147:              this.groupBox1.Font = new System.Drawing.Font("Microsoft Sans Serif", 10F,
                       System.Drawing.FontStyle.Underline, System.Drawing.GraphicsUnit.Point,
                       ((byte)(0)));
OR148:              this.groupBox1.Location = new System.Drawing.Point(28, 120);
OR149:              this.groupBox1.Name = "groupBox1";
OR150:              this.groupBox1.Size = new System.Drawing.Size(488, 64);
OR151:              this.groupBox1.TabIndex = 5;
OR152:              this.groupBox1.TabStop = false;
OR153:              this.groupBox1.Text = "Messages From Our Grandmothers";
OR154:              this.groupBox1.Enter += new System.EventHandler(this.groupBox1_Enter);
                    //
                    // label7
                    //
OR155:              this.label7.AutoSize = true;
OR156:              this.label7.Font = new System.Drawing.Font("Microsoft Sans Serif", 10F,
                       System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point,
                       ((byte)(0)));
OR157:              this.label7.Location = new System.Drawing.Point(75, 40);
OR158:              this.label7.Name = "label7";
OR159:              this.label7.Size = new System.Drawing.Size(0, 0);
OR160:              this.label7.TabIndex = 3;
                    //
                    // label6
                    //
OR161:              this.label6.AutoSize = true;
OR162:              this.label6.Font = new System.Drawing.Font("Microsoft Sans Serif", 10F,
                       System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point,
                       ((byte)(0)));
OR163:              this.label6.Location = new System.Drawing.Point(75, 20);
OR164:              this.label6.Name = "label6";
OR165:              this.label6.Size = new System.Drawing.Size(0, 0);
OR166:              this.label6.TabIndex = 2;
                    //
                    // label5
                    //
OR167:              this.label5.AutoSize = true;
OR168:              this.label5.Font = new System.Drawing.Font("Microsoft Sans Serif", 10F,
                       System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point,
                       ((byte)(0)));
OR169:              this.label5.Location = new System.Drawing.Point(16, 40);
OR170:              this.label5.Name = "label5";
OR171:              this.label5.Size = new System.Drawing.Size(52, 17);
OR172:              this.label5.TabIndex = 1;
OR173:              this.label5.Text = "Juliette:";
                    //
                    // label4
                    //
OR174:              this.label4.AutoSize = true;
OR175:              this.label4.Font = new System.Drawing.Font("Microsoft Sans Serif", 10F,
                       System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point,
                       ((byte)(0)));
OR176:              this.label4.Location = new System.Drawing.Point(16, 20);
OR177:              this.label4.Name = "label4";
OR178:              this.label4.Size = new System.Drawing.Size(49, 17);
OR179:              this.label4.TabIndex = 0;
OR180:              this.label4.Text = "Moses:";
                    //
                    // button1
                    //
OR181:              this.button1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F,
                       System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point,
                       ((byte)(0)));
OR182:              this.button1.Location = new System.Drawing.Point(452, 50);
OR183:              this.button1.Name = "button1";
OR184:              this.button1.Size = new System.Drawing.Size(60, 22);
OR185:              this.button1.TabIndex = 6;
OR186:              this.button1.Text = "Send";
OR187:              this.button1.Click += new System.EventHandler(this.button1_Click);
                    //
                    // button2
                    //
OR188:              this.button2.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F,
                       System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point,
                       ((byte)(0)));
OR189:              this.button2.Location = new System.Drawing.Point(452, 84);
OR190:              this.button2.Name = "button2";
OR191:              this.button2.Size = new System.Drawing.Size(60, 22);
OR192:              this.button2.TabIndex = 7;
OR193:              this.button2.Text = "Send";
OR194:              this.button2.Click += new System.EventHandler(this.button2_Click);
                    //
                    // button3
                    //
OR195:              this.button3.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F,
                       System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point,
                       ((byte)(0)));
OR196:              this.button3.Location = new System.Drawing.Point(50, 195);
OR197:              this.button3.Name = "button3";
OR198:              this.button3.Size = new System.Drawing.Size(200, 23);
OR199:              this.button3.TabIndex = 8;
OR200:              this.button3.Text = "Going to Grandma Moses\' House";
OR201:              this.button3.Click += new System.EventHandler(this.button3_Click);
                    //
                    // button4
                    //
OR202:              this.button4.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F,
                       System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point,
                       ((byte)(0)));
OR203:              this.button4.Location = new System.Drawing.Point(300, 195);
OR204:              this.button4.Name = "button4";
OR205:              this.button4.Size = new System.Drawing.Size(200, 23);
OR206:              this.button4.TabIndex = 9;
OR207:              this.button4.Text = "Going to Grandma Juliette\'s House";
OR208:              this.button4.Click += new System.EventHandler(this.button4_Click);
                    //
                    // button5
                    //
OR209:              this.button5.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F,
                       System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point,
                       ((byte)(0)));
OR210:              this.button5.Location = new System.Drawing.Point(208, 226);
OR211:              this.button5.Name = "button5";
OR212:              this.button5.Size = new System.Drawing.Size(125, 23);
OR213:              this.button5.TabIndex = 10;
OR214:              this.button5.Text = "Quit this Project";
OR215:              this.button5.Click += new System.EventHandler(this.button5_Click);
                    //
                    // Form1
                    //
OR216:              this.AutoScaleDimensions = new System.Drawing.SizeF(5F, 13F);
OR217:              this.ClientSize = new System.Drawing.Size(542, 266);
OR218:              this.Controls.Add(this.button5);
OR219:              this.Controls.Add(this.button4);
OR220:              this.Controls.Add(this.button3);
OR221:              this.Controls.Add(this.button2);
OR222:              this.Controls.Add(this.button1);
OR223:              this.Controls.Add(this.groupBox1);
OR224:              this.Controls.Add(this.textBox2);
OR225:              this.Controls.Add(this.textBox1);
OR226:              this.Controls.Add(this.label3);
OR227:              this.Controls.Add(this.label2);
OR228:              this.Controls.Add(this.label1);
OR229:              this.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F,
                       System.Drawing.FontStyle.Underline, System.Drawing.GraphicsUnit.Point,
                       ((byte)(0)));
OR230:              this.Name = "Form1";
OR231:              this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
OR232:              this.Text = "Over The River and Through The Woods";
OR233:              this.groupBox1.ResumeLayout(false);
OR234:              this.groupBox1.PerformLayout();
OR235:              this.ResumeLayout(false);
OR236:              this.PerformLayout();
OR237:            }

OR238:            #endregion
OR239:            private System.Windows.Forms.Label label1;
OR240:            private System.Windows.Forms.Label label2;
OR241:            private System.Windows.Forms.Label label3;
OR242:            private System.Windows.Forms.TextBox textBox1;
OR243:            private System.Windows.Forms.TextBox textBox2;
OR244:            private System.Windows.Forms.GroupBox groupBox1;
OR245:            private System.Windows.Forms.Label label4;
OR246:            private System.Windows.Forms.Button button1;
OR247:            private System.Windows.Forms.Button button2;
OR248:            private System.Windows.Forms.Button button3;
OR249:            private System.Windows.Forms.Button button4;
OR250:            private System.Windows.Forms.Button button5;

                  // These next two labels must be converted from private to public:
OR251:            public System.Windows.Forms.Label label6;
OR252:            public System.Windows.Forms.Label label7;
OR253:            private System.Windows.Forms.Label label5;
OR254:          }
OR255:        }
//=========================================================================================//
Form2.cs:
OR300:        using System;
OR301:        using System.Collections.Generic;
OR302:        using System.ComponentModel;
OR303:        using System.Data;
OR304:        using System.Drawing;
OR305:        using System.Text;
OR306:        using System.Windows.Forms;

OR307:        namespace OverTheRiverAndThroughTheWoods
OR308:        {
OR309:          partial class Form2 : Form
OR310:          {
                  // This is a programmer-added item:
OR311:            public Form1 frm1Parent;
                  //
OR312:            public Form2(Form1 frm1)
OR313:            {
OR314:              frm1Parent = frm1;
OR315:              InitializeComponent();
OR316:            }
//-----------------------------------------------------------------------------------------//
OR320:            private void Form2_Load(object sender, EventArgs e)
OR321:            { // Place the main window's message into 'label2'.
OR322:              label2.Text = frm1Parent.strGrandmaMoses;
OR323:            }
//----------------------------------------------------------------------------------------//
OR330:            private void button1_Click(object sender, EventArgs e)
OR331:            { // Place the return message into persistent storage in Form1:
OR332:              frm1Parent.label6.Text = textBox1.Text;
OR333:              Close();
OR334:            }
OR335:          }
OR336:        }
//========================================================================================//
Form2.Designer.cs:
OR360:        namespace OverTheRiverAndThroughTheWoods
OR361:        {
OR362:          partial class Form2
OR363:          {
                  // Required designer variable.
OR364:            private System.ComponentModel.IContainer components = null;
                  // Clean up any resources being used.
OR365:            protected override void Dispose(bool disposing)
OR366:            {
OR367:              if (disposing && (components != null))
OR368:              {
OR369:                components.Dispose();
OR370:              }
OR371:              base.Dispose(disposing);
OR372:            }

OR373:            #region Windows Form Designer generated code
                  // Required method for Designer support - do not modify
OR374:            private void InitializeComponent()
OR375:            {
OR376:              this.label1 = new System.Windows.Forms.Label();
OR377:              this.label2 = new System.Windows.Forms.Label();
OR378:              this.label3 = new System.Windows.Forms.Label();
OR379:              this.textBox1 = new System.Windows.Forms.TextBox();
OR380:              this.button1 = new System.Windows.Forms.Button();
OR381:              this.SuspendLayout();
                    //
                    // label1
                    //
OR382:              this.label1.AutoSize = true;
OR383:              this.label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 10F,
                       System.Drawing.FontStyle.Underline, System.Drawing.GraphicsUnit.Point,
                       ((byte)(0)));
OR384:              this.label1.Location = new System.Drawing.Point(16, 16);
OR385:              this.label1.Name = "label1";
OR386:              this.label1.Size = new System.Drawing.Size(140, 17);
OR387:              this.label1.TabIndex = 0;
OR388:              this.label1.Text = "Message From Home:";
                    //
                    // label2
                    //
OR389:              this.label2.AutoSize = true;
OR390:              this.label2.Font = new System.Drawing.Font("Microsoft Sans Serif", 10F,
                       System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point,
                       ((byte)(0)));
OR391:              this.label2.Location = new System.Drawing.Point(16, 40);
OR392:              this.label2.Name = "label2";
OR393:              this.label2.Size = new System.Drawing.Size(0, 0);
OR394:              this.label2.TabIndex = 1;
                    //
                    // label3
                    //
OR395:              this.label3.AutoSize = true;
OR396:              this.label3.Font = new System.Drawing.Font("Microsoft Sans Serif", 10F,
                       System.Drawing.FontStyle.Underline, System.Drawing.GraphicsUnit.Point,
                       ((byte)(0)));
OR397:              this.label3.Location = new System.Drawing.Point(16, 72);
OR398:              this.label3.Name = "label3";
OR399:              this.label3.Size = new System.Drawing.Size(156, 17);
OR400:              this.label3.TabIndex = 2;
OR401:              this.label3.Text = "Send Message to Home:";
                    //
                    // textBox1
                    //
OR402:              this.textBox1.Font = new System.Drawing.Font("Microsoft Sans Serif", 10F,
                       System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point,
                       ((byte)(0)));
OR403:              this.textBox1.Location = new System.Drawing.Point(24, 94);
OR404:              this.textBox1.Name = "textBox1";
OR405:              this.textBox1.Size = new System.Drawing.Size(340, 23);
OR406:              this.textBox1.TabIndex = 3;
                    //
                    // button1
                    //
OR407:              this.button1.Location = new System.Drawing.Point(137, 128);
OR408:              this.button1.Name = "button1";
OR409:              this.button1.Size = new System.Drawing.Size(125, 23);
OR410:              this.button1.TabIndex = 4;
OR411:              this.button1.Text = "Return to Home";
OR412:              this.button1.Click += new System.EventHandler(this.button1_Click);
                    //
                    // Form2
                    //
OR413:              this.AutoScaleDimensions = new System.Drawing.SizeF(5F, 13F);
OR414:              this.ClientSize = new System.Drawing.Size(392, 166);
OR415:              this.Controls.Add(this.button1);
OR416:              this.Controls.Add(this.textBox1);
OR417:              this.Controls.Add(this.label3);
OR418:              this.Controls.Add(this.label2);
OR419:              this.Controls.Add(this.label1);
OR420:              this.Name = "Form2";
OR421:              this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
OR422:              this.Text = "Grandma Moses\' House";
OR423:              this.Load += new System.EventHandler(this.Form2_Load);
OR424:              this.ResumeLayout(false);
OR425:              this.PerformLayout();
OR426:            }

OR427:            #endregion

OR428:            private System.Windows.Forms.Label label1;
OR429:            private System.Windows.Forms.Label label2;
OR430:            private System.Windows.Forms.Label label3;
OR431:            private System.Windows.Forms.TextBox textBox1;
OR432:            private System.Windows.Forms.Button button1;
OR433:          }
OR434:        }
//=========================================================================================//
Form3.cs:
OR470:        using System;
OR471:        using System.Collections.Generic;
OR472:        using System.ComponentModel;
OR473:        using System.Data;
OR474:        using System.Drawing;
OR475:        using System.Text;
OR476:        using System.Windows.Forms;

OR477:        namespace OverTheRiverAndThroughTheWoods
OR478:        {
OR479:          partial class Form3 : Form
OR480:          {
                  // This is a programmer-added item:
OR481:            public Form1 frm1Parent;
                  //
OR482:            public Form3(Form1 frm1)
OR483:            {
OR484:              frm1Parent = frm1;
OR485:              InitializeComponent();
OR486:            }
//-----------------------------------------------------------------------------------------//
OR500:            private void Form3_Load(object sender, EventArgs e)
OR501:            { // Place the main window's message into 'label2'.
OR502:              label2.Text = frm1Parent.strGrandmaJuliette;
OR503:            }
//-----------------------------------------------------------------------------------------//
OR510:            private void button1_Click(object sender, EventArgs e)
OR511:            { // Place the return message into persistent storage in Form1:
OR512:              frm1Parent.label7.Text = textBox1.Text;
OR513:              Close();
OR514:            }
OR515:          }
OR516:        }
//=========================================================================================//
Form3.Designer.cs

[This is a repeat of Form2 Designer, replacing Grandma Moses with Grandma Juliette.
//=========================================================================================//


One-Way Communication: Using Arguments

There is a second way to pass information from the main window to the child window. This is called one-way communication, and it uses arguments. Most programmers are familiar with this method because it was invented in the 1960s. Here is how it works:

  1. Place arguments into the Form2 creation statement (from Form1):

//   Create the Form2 modal window.
//   Send 3 arguments to Form2: int, char, string.
Form2 frm2 = new Form2(kkForm1, intWeek, intYear);
frm2.ShowDialog();
frm2.Close();

  2. Receive the arguments in Form2:

public Form2(int TNumber1, int TNumber2, int TNumber3)
{
  FileNumber = TNumber1;
  iWeek = TNumber2;
  iYear = TNumber3;

The only data types you can pass this way are the integer, float, double, and decimal. Character and string arrays cannot be sent or received.

Note

If things go badly for you during this build of OverTheRiverAndThroughTheWoods, you will find a complete copy of the project in Visual Studio 2005\Projects\DemosSourceCode if you copied all the demo code from the companion files to your PC hard drive as described in the Introduction.


Project Cleanup

If you left the words “label6” and “label7” on the main window, this is a good time to replace the words with blanks. To do this, click on the first label, move to the Properties window, and replace the words with blanks in the Text box. Repeat the procedure for the second label.


Chapter 6: Visual Studio C# Code Placement

Overview

Visual Studio C# code placement in FormX.cs source code is not greatly different from C or C++ code placement, but there are a few changes that should be mentioned. For this exercise, move to the desktop, bring up the IDE, and click on the File main menu item, followed by Open, then Project/Solution. When the Open Project window opens, go to Visual Studio 2005\Projects\DemosSourceCode. Click on the CodePlacement folder, and then click on the CodePlacement.sln file (which is the highlighted file in the folder). This opens the project in the IDE.

Click on the Build item in the main menu, then Build Solution (which compiles the project). After compilation, click on the Debug main menu item and select Start Debugging (which runs the project). This project has two windows — a main window and a child window. Some of the lines of code in Form1.cs and Form2.cs have been tagged at the left edge of the source code so the instructions in this chapter will lead you directly to a particular line of code. The main window looks like this:

Image from book
Figure 6-1: Code Placement, main window

The child window that is created from the main window is shown in Figure 6-2.

Image from book
Figure 6-2: Child window

The project is initially set up so the main window and child window can pass information to each other, using lines CP204, CP301, CP401, and CP402. (The source code is listed at the end of this chapter.)


Demonstrations

Demo #1: Initializing an Integer

This project is initially set up so an integer intNumber is declared in line CP011. However, it is not assigned a number until the program reaches line CP103 in event handler button1_Click.

  1. Do not click on the Start Demo button on the main window this first time. Instead, click directly on the Call Child Window button. This should produce a message box with the message shown in Figure 6-3.

    Image from book
    Figure 6-3: Message box #1

    This message comes from line CP603 in Form 2, Form2_Load. CP603 has the value 0 for intAnswer, which it received from CP602. CP602 got the 0 from frmParent.intNumber. intNumber is declared in Form1 at line CP011. But who put the value 0 in intNumber? The compiler.

    The message box appears before the child window appears because any code executed in FormX_Load always executes before its window is displayed. When you click on the OK button in the message box, the child window appears and is created.

  2. Click on the OK button to remove message box #1, then click on the Return button to exit the child window.

  3. When the main window appears, click first on the Start Demo button, then on the Call Child Window button. The message box appears again, but this time the value of intNumber is 92.

    Image from book
    Figure 6-4: Message box #2

    This time there is no mystery about the new intNumber value of 92. The variable was assigned the number 92 in line CP103 in event handler button1_Click, which is the Start Demo button.

  4. Click on the OK button in message box #2, then on the Return button to close the child window. Next, click on Quit to close the project.

    So we conclude that if you declare an integer in the class (line CP011) and forget to assign it a number, then the compiler will placea0inthat location to keep your program running. This is fine, but it only works with integers. The compiler will not place its own default numbers or characters in string or char arrays, float, double, etc.

  5. In the IDE, press the F7 key to display the Form1 source code. Change line CP011 from public int intNumber; to public int intNumber = 6;. This assigns an initial value to this variable.

  6. Recompile and rerun the program two more times. The first time, move directly to the Call Child Window button, which produces the message “intNumber from Form 1 = 6.” The second time, click on Start Demo followed by Call Child Window. This time the MessageBox answers “intMessage from Form 1 = 92” since you initialized intNumber in line CP011.

  7. Is it necessary to place the word “public” in front of the declaration in line CP011? Move to line CP011 and delete the word “public.” Then recompile and rerun. What happens?

    You immediately get a compiler error when you attempt to compile, because you cannot pass information between windows unless that information (variable intNumber) is declared as a public member of a class. It is listed in the class listing, but it has to have the designation “public” to make it a candidate for access by other windows (the child window).

  8. Leave line CP011 as it is (with the word “public” deleted), and move to Form2.cs and lines CP602 and 603. Comment out these two lines so they are no longer executable. The lines should look like this:

/* CP602 */      // int intAnswer = frmParent.intNumber;
/* CP603 */      // MessageBox.Show("intNumber from Form1 = "
                 // + intAnswer.ToString() + " .");

    Recompile and rerun.

    Why did the project run correctly this time? In the first case, the compiler saw that variable intNumber was supposed to be passed to Form2, but you cannot pass a variable that is not a public member of the Form1 class. So the compiler balked. But in the second case, the variable was not required by Form2, and Form2 did not “ask” for frmParent.intNumber (since you had commented out lines CP602-CP603) and the project ran OK.

1.

Can you switch the designation “public” in line CP011 to “private” and still pass the variable to Form2? Try this. Remove the comment forward slashes on lines CP602-CP603, and replace “int intNumber = 6;” in line CP011 with “private intNumber = 6;”. Will this combination compile?

Image from book

Answers

1.

No. Only a public member can be passed to another window. Change the “private” designation in line CP011 back to “public.”

Demo #2: Creating a Variable That Is Global to Form1

  1. In line CP012, remove the double forward slashes in front of this declaration:

int intGlobalNumber = 77;

  2. In line CP104 in the Form 1 button1_Click event handler, remove the double forward slashes from this message box:

/* CP104 */      MessageBox.Show("intGlobalNumber = "
                 + intGlobalNumber.ToString()+ " .");

    Recompile the program and rerun. Does the message box have access to intGlobalString? Yes. As soon as you click on the Start Demo button, the message box shown in Figure 6-5 appears.

    Image from book
    Figure 6-5: Message box #3

    So we conclude that you can create variables that are global to a given form by listing them in the class list at the top of the *.cs file, and it doesn’t matter whether or not they are declared as public. Interestingly enough, a variable that is undeclared (with neither public or private) reverts to a private variable — one that can only be used within the confines of this form.

    But what if we declare intGlobalNumber somewhere else, like in the public Form1 area just above or below the InitializeCom-ponent() statement?

  3. Cut the statement “int intGlobalNumber = 77;” from line CP012 and paste it at line CP016. Recompile.

    Does this work? The answer is no. Integer intGlobalNumber has to be declared under the public class Form1 listing (CP010) to become a global variable (but it needs no “public” designation unless it is needed by another window form).

1.

Is line CP016 not a part of the public class Form1 listing?

Image from book

Answers

1.

By placing the statement in the CP016 slot, which is inside the curly brackets of public Form1(), we defeat this declaration; it is inside the public Form1() curly brackets! As long as “int intGlobalNumber = 77” remains within the curly brackets at CP016, it is not a good declaration.

  4. Move “int intGlobalNumber = 77;” from line CP016 back to CP012, where it belongs. Now the compiler will recognize this declaration. Is there any other place we can put int intGlobalNumber = 77 and have the compiler recognize this declaration?

  5. Cut line CP012 (int intGlobalNumber = 77;) and move it down to line CP099 (which is an open space between the static main procedure and the first event handler, button1_Click, at line CP101). Having placed this statement at CP099, is this an acceptable global declaration?

Yes, this works. But why does it work? Because this particular declaration, “int intGlobalNumber = 77;”, can occupy a space at either CP012 or CP099 (but not both). CP099 is simply a continuation of the partial class Form1 declaration that begins at line CP010. In either location the designation “public” or “private” is not required (but it defaults to a private declaration).

If this declaration works properly at line CP012 or CP099, why won’t it work within the public Form1() function at line CP016? Because public Form1() is a function, not a declaration. This same statement, “int intGlobal…,” would not work properly in any of the functions described in the class, including “protected void override Dispose()” and “private void InitializeComponent().”

So what purpose does public Form1() serve (besides containing the procedure InitializeComponent())? To answer this question we pull some code from an old demonstration project named TimerTimer.

XT016:      public Form1()
            {
XT017:        Timer timer = new Timer();
              // Next line: 1,000 milliseconds between ticks, or 1 per second.
XT018:        timer.Interval = 1000;
XT019:        timer.Tick += new EventHandler(timer1_Tick);
XT020:        timer.Start(); // Alternate statement is 'timer.Enabled = true;'.

XT021:        InitializeComponent();
XT022:      }

As it turns out, the programmer could just as well have created an event handler in the TimerTimer project named Form1_Load, and placed these lines of code (XT016-020) into that handler. The Form1_Load handler executes just as soon as the main window is created, so the timer would be started there. Therefore, one of the purposes of public Form1() in the middle of the class declaration is to perform the same initializing functions that Form1_Load handles.

1.

Can the call to execute the InitializeComponent() function just above line CP016 be placed anywhere except where it is (for example, in a Form1_Load function)?

No, you must leave the InitializeComponent() exactly where it is!

Answers

1.

No, you must leave the InitializeComponent() exactly where it is!

To create the Form1_Load() method (subroutine), move to the Form1 window and highlight it (to place its properties in the Properties window). At the top of the Properties window, click on the Events icon to bring up the event handlers. Under Behavior, find Load, and double-click on it. This places the Form1_Load() method into the Form1 source code.

Some programmers abhor the FormX_Load event handler so they place all the startup information in the public FormX() function at the top of the file. The project works fine either way.

If you are like most programmers, you become annoyed when you expect a variable in a project to be available to you at the point at which you are currently working, and the compiler gives you one of those “cannot identify” errors concerning that variable. This means that the declaration of that variable has to be moved to a point within the top-level class declaration. If the variable is not needed by other windows (forms), then it need not be declared as public, but it should be moved to the top of the form to ensure that it is available to all elements within the form. If you declare the variable as neither public nor private, it reverts to a private variable. By top of the form, we mean precisely one location:

partial class FormX  : Form
{
  // RIGHT HERE !! //

  public FormX()
  {
    InitializeComponent();
  etc.

Your code will be easier to read if you place all the declarations together, as shown above.


A Common For Statement Error

A common for error is to assume globality that doesn’t exist. Consider the following for statement, which may be exited after 300 cycles or when the if statement inside is satisfied:

'int ItemNumber' receives a value here.

for (int jj = 0; jj < 300; jj++)
{
  if(ItemNumber == StorageStack[jj]) goto JUMP;
}
JUMP:
// Ready to proceed.

When the sequence arrives at JUMP: we tend to assume that we now know which StorageStack() contains the ItemNumber set at the top of the code. So we finish the action with statements like:

if(jj < 300) { Do something..... }
else { Do something different..... }

We know that the iteration variable in the for statement, jj, is incremented at the bottom of the loop, and when it reaches the value 300 the loop will fail. Hence, we decide to test jj after the JUMP: statement to see if we found what were looking for.

The Visual Studio C# compiler will nail you in a microsecond! It will tell you that the jj found in the statement “if(jj < 300)” cannot be identified.

What is wrong with this code? The int jj that appears in the first line of the for statement is a local variable that services only this for loop. As soon as the for loop is exited, variable jj ceases to exist. You must introduce a separate, global integer variable that is declared earlier — a variable that “remembers” the value of jj when the for loop is exited:

for(int jj = 0; jj < 300; jj++)
{
  if(ItemNumber == StorageStack[jj])
  {
    RememberNumber = jj;
    goto JUMP;
  }
}
JUMP:
// Ready to proceed.
if(RememberNumber < 300) { Do something..... }
else { Do something else..... }

All programmers do strange things like this from time to time.

CodePlacement (CP)

Form1.cs:
CP001:          using System;
CP002:          using System.Collections.Generic;
CP003:          using System.ComponentModel;
CP004:          using System.Data;
CP005:          using System.Drawing;
CP006:          using System.Windows.Forms;

/*CP009*/     namespace CodePlacement
              {
/*CP010*/       partial class Form1 : Form
                {
/*CP011*/         public int intNumber; // = 6;
/*CP012*/         // int intGlobalNumber = 77;
/*CP013*/         public int[] intArray = new int[] {1, 22, 88};

/*CP014*/         public Form1()
                  {
                    InitializeComponent();
/*CP016*/
/*CP017*/
                  }

/*CP099*/         // int intGlobalNumber = 77;

//-----------------------------------------------------------------------------------------//
/*CP101*/         private void button1_Click(object sender, EventArgs e)
/*CP102*/         { // Start Demo.
/*CP103*/           intNumber = 92;
/*CP104*/           // MessageBox.Show("intGlobalNumber = "
                    // +intGlobalNumber.ToString() + " .");
/*CP105*/         }
//-----------------------------------------------------------------------------------------//
/*CP201*/         private void button2_Click(object sender, EventArgs e)
/*CP202*/         { // Call Child Window. Open Form2.
/*CP203*/           // 'this' below sends Form1 'handle' to Form2.
/*CP204*/           Form2 frm2 = new Form2(this );
/*CP205*/           frm2.ShowDialog();
/*CP206*/           frm2.Close();
                  }
//-----------------------------------------------------------------------------------------//
                  private void button3_Click(object sender, EventArgs e)
                  { // Quit.
                    Close();
                  }
                }
              }
//=========================================================================================//
Form1.Designer.cs
CP300:        namespace CodePlacement
              {
                partial class Form1
                {
                  // Required designer variable.
                  private System.ComponentModel.IContainer components = null;
                  // Clean up any resources being used.
                  protected override void Dispose(bool disposing)
                  {
                    if (disposing && (components != null)) components.Dispose();
                    base.Dispose(disposing);
                  }

                  #region Windows Form Designer generated code
                  // Required method for Designer support.
                  private void InitializeComponent()
                  {
                    this.label1 = new System.Windows.Forms.Label();
                    this.button1 = new System.Windows.Forms.Button();
                    this.button2 = new System.Windows.Forms.Button();
                    this.button3 = new System.Windows.Forms.Button();
                    this.SuspendLayout();
                    //
                    // label1
                    //
                    this.label1.AutoSize = true;
                    this.label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F,
                    ((System.Drawing.FontStyle)((System.Drawing.FontStyle.Bold |
                    System.Drawing.FontStyle.Underline))), System.Drawing.GraphicsUnit.Point,
                    ((byte)(0)));
                    this.label1.Location = new System.Drawing.Point(57, 16);
                    this.label1.Name = "label1";
                    this.label1.Size = new System.Drawing.Size(264, 21);
                    this.label1.TabIndex = 0;
                    this.label1.Text = "Visual Studio C# Code Placement";
                    //
                    // button1
                    //
                    this.button1.Location = new System.Drawing.Point(58, 70);
                    this.button1.Name = "button1";
                    this.button1.Size = new System.Drawing.Size(100, 23);
                    this.button1.TabIndex = 1;
                    this.button1.Text = "Start Demo";
                    this.button1.Click += new System.EventHandler(this.button1_Click);
                    //
                    // button2
                    //
                    this.button2.Location = new System.Drawing.Point(204, 70);
                    this.button2.Name = "button2";
                    this.button2.Size = new System.Drawing.Size(125, 23);
                    this.button2.TabIndex = 2;
                    this.button2.Text = "Call Child Window";
                    this.button2.Click += new System.EventHandler(this.button2_Click);
                    //
                    // button3
                    //
                    this.button3.Location = new System.Drawing.Point(153, 114);
                    this.button3.Name = "button3";
                    this.button3.Size = new System.Drawing.Size(76, 23);
                    this.button3.TabIndex = 3;
                    this.button3.Text = "Quit";
                    this.button3.Click += new System.EventHandler(this.button3_Click);
                    //
                    // Form1
                    //
                    this.AutoScaleDimensions = new System.Drawing.SizeF(5F, 13F);
                    this.ClientSize = new System.Drawing.Size(374, 158);
                    this.Controls.Add(this.button3);
                    this.Controls.Add(this.button2);
                    this.Controls.Add(this.button1);
                    this.Controls.Add(this.label1);
                    this.Name = "Form1";
                    this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
                    this.Text = "Code Placement";
                    this.ResumeLayout(false);
                    this.PerformLayout();
                  }

                  #endregion
                  private System.Windows.Forms.Label label1;
                  private System.Windows.Forms.Button button1;
                  private System.Windows.Forms.Button button2;
                  private System.Windows.Forms.Button button3;
                }
              }
//=========================================================================================//
Form2.cs
              [7 lines]

              namespace CodePlacement
              {
/*CP301*/       partial class Form2 : Form
                {
                  public Form1 frmParent;

/*CP401*/         public Form2(Form1 frm)
                  {
/*CP402*/           frmParent = frm;
/*CP403*/           InitializeComponent();
/*CP404*/
/*CP405*/
/*CP406*/         }
//-----------------------------------------------------------------------------------------//
                  private void button1_Click(object sender, EventArgs e)
                  { // Return to main.
                    Close();
                  }
//-----------------------------------------------------------------------------------------//
/*CP601*/         private void Form2_Load(object sender, EventArgs e)
                  {
/*CP602*/           int intAnswer = frmParent.intNumber;
/*CP603*/           MessageBox.Show("intNumber from Form1 = " +
                    intAnswer.ToString() + " .");
                  }
                }
              }
//=========================================================================================//
Form2.Designer.cs
              namespace CodePlacement
              {
                partial class Form2
                {
                  // Required designer variable.
                  private System.ComponentModel.IContainer components = null;
                  // Clean up any resources being used.
                  protected override void Dispose(bool disposing)
                  {
                    if (disposing && (components != null)) components.Dispose();
                    base.Dispose(disposing);
                  }

                  #region Windows Form Designer generated code
                  // Required method for Designer support.
                  private void InitializeComponent()
                  {
                    this.button1 = new System.Windows.Forms.Button();
                    this.SuspendLayout();
                    //
                    // button1
                    //
                    this.button1.Location = new System.Drawing.Point(106, 42);
                    this.button1.Name = "button1";
                    this.button1.TabIndex = 0;
                    this.button1.Text = "Return";
                    this.button1.Click += new System.EventHandler(this.button1_Click);
                    //
                    // Form2
                    //
                    this.AutoScaleDimensions = new System.Drawing.SizeF(5F, 13F);
                    this.ClientSize = new System.Drawing.Size(292, 107);
                    this.Controls.Add(this.button1);
                    this.Name = "Form2";
                    this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
                    this.Text = "Child Window";
                    this.Load += new System.EventHandler(this.Form2_Load);
                    this.ResumeLayout(false);
                  }

                  #endregion
                  private System.Windows.Forms.Button button1;
                }
              }

If you want to redo the CodePlacement project (and are concerned that the code in this project has been changed by your actions), then find the Form1Spare.cs file in this project and make a copy of it, change the copy’s name to Form1.cs, and overwrite the old Form1.cs that you have altered.


Chapter 7: Classes, Constructors, Overloading, and Help

Digital Computer Software Class Creation

Programming language classes have been in existence since the first programming languages came into being (FORTRAN, COBOL, Pascal, etc.). Each class consisted of a series of machine code statements that were inserted into memory by the compiler to achieve the assigned task. For example, the initial “printf” FORTRAN statement that sent text to a printer initiated a sequence of 795 8-byte (64-bit) machine code statements when the program was loaded into the computer. When the 8-byte word was later replaced by a 32-byte word in the early 1990s, the number of words in the printf machine code set was reduced to 80 statements.

It was not unusual to find that printf() at a major compiler manufacturer was the sole responsibility of one programmer/analyst and he or she either became rich and famous or was drummed out of the programmer corps, based on the performance of printf(). (This was a particularly nasty class because printer manufacturers kept switching their required inputs for their printers. Microsoft put a stop to this wild-catting in the late 1990s.)

These classes (like printing, file services, logic, etc.) were placed in large machine code files that were subsequently included in the compiling process by the famous *.h declaration files. The compiler copied the machine code that it needed into the executable file and ignored the rest. This system (machine-code files that supported each of the statements in a compiler) worked well for a generation or two because the machine-code builders specified a fixed number of input parameters (arguments) for each of the mini tasks that a computer performed.

Then their world fell apart when some high-minded computerists demanded the capability to “overload” these familiar functions — providing the programmer with a basic function that allowed as little as one input parameter and a maximum of 10 to 12 input parameters. The programmer used whatever form of the machine-code function was most useful at the time. The concept was simple, but the implementation of the concept in the world of machine-code creation was mind-boggling. Mind-boggling or not, the basic computer tasks were all successfully “overloaded” over a period of several years.

With the advent of extremely high-speed CPUs in the 1990s and 2000s that were capable of addressing billions of memory locations on one’s computer, it was time to look seriously at the object-oriented programming concepts that a compiler language named Smalltalk had introduced in the early ’90s. Smalltalk’s secret was that it found a way to instantiate a portion of a software class and create an object that was a sequence of machine code commands, ready to perform in one’s computer on command. The object was allowed to float out in memory until it was needed.

Today we instantiate (create) objects with the universal compiler word “new.”


Public, Private, and Unspecified Classes

The designator “public” at the start of a class declaration in a form (window) means that the object (for example, a variable or function) may be accessed by other forms if communication between the form that originally declares the object and the form that needs to use the object has been established (as shown in Chapter 5).

The designator “private” at the start of a class declaration in a form means that the object may be used only within the form where the object is declared. An object that is undeclared in the class list (neither public nor private) defaults to a private class.

The programmer never sees the bulk of the machine code or intermediate code form, and if it is printed out the result means nothing to a human being. This was true in the old days, and it remains true today. By the late 1970s most compiler manufacturers placed these necessary machine code support files at the disposal of the programmer by including their filenames in the *.h declarations files. For example, all the machine code that supported creating, reading, or writing to files was placed in one huge machine code *.h file with a name like “FileSupport.” By including the FileSupport file in a *.h file listing that supported one particular form, all the FileSupport machine code was placed at the disposal of the compiler at compile time. The compiler extracted all the machine code required to support that form and released the rest. The *.h files were the keepers of all class declarations.

But there was another wide use of include files: If Form1 in the project needed access to variables that were declared in Form2, the programmer could make the Form2 variables available to Form1 simply by including Form2’s *.h file in the Form1 include list. This concept worked because the *.h files contained all the information on classes and kept a list of where each variable was located in memory. So the exchange of *.h declaration files between forms in a project was a convenient method for giving all forms access to all variables in the project (if the programmer wanted this to happen). However, this widespread practice was a total disregard of the principles of object-oriented programming (OOP), and some compiler architects called for an end to this proliferation of *.h declaration files.

Microsoft Visual C# shut down the easy data exchanges between forms by demanding that their compilers conform to all the OOP standards, namely, that if Form2 needed access to a variable or procedure in Form1, then Form1 had to create a handle to itself (a key to unlock the path between Form1 and Form2), hand the key to Form2, and now Form2 could access the variables and procedures in Form1. This is the way Visual C# conducts business. This method of data exchange was demonstrated in Chapter 5.

The Visual C# compiler shows you two views of a window’s construct: a code display (source code) and a window template display. The older compilers showed you three displays: a code display, a *.h declarations file display, and a window template display. In Visual C# the *.h declarations have been moved to the *.Designer.cs file. The definitions for all controls on a window template are split — part of the code appears in the source file *.cs and part appears in the *.Designer.cs file, which is a file closed to the programmer (only the IDE writes to this file, except under unusual circumstances).

In the Visual C# world the machine code files are no longer included; they are brought to the compilation with the “using” statements at the top of the source code file.


Object-Oriented Programming (OOP)

One difference between the handling of computer machine code and local variables prior to the 1990s and the handling of that same code and variables after the advent of OOP is this: Before the 1990s, the code to support a given window stood apart from the storage locations for variables used in that code. The code was put into machine code form at the time of compilation and was used throughout the execution of the project. Its local variables were stored elsewhere. With OOP, both the code and its local variables are bundled into one packet and are recreated by the PC’s operating system each time the packet is needed. OOP works wonderfully so long as the PC has lots of memory and the project is equipped with a garbage collector that destroys old, unused packets and makes the old memory locations available to the operating system. The Visual C# compiler uses OOP exclusively in its output formulation (creating the intermediate code that is transformed into machine code by a special cookie on one’s computing machine).


The Six “Using” Statements

When you begin a new project, that new project is always created with one window (the main window) if you select Windows Application in the New Project window. When the source code file is created for the main window and then displayed, there are six “using” statements at the top of the file:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Windows.Forms;

System is a several-megabyte group of over a thousand files. But System alone cannot provide all the machine code your project needs.

System.Collections.Generic contains all the constructs that a project may need (like a definition for file length when a file is named in the source code).

System.ComponentModel contains definitions for all the components that are at the programmer’s disposal, like creating a database in the middle of one’s source code.

System.Data contains definitions for all data types (like the definition for ASCII in a StreamWriter statement). The principal reason for Data in Visual C# is to support database operations.

System.Drawing contains all the instructions to place controls (like buttons) on a window template.

System.Windows.Forms contains definitions for all Windows forms, including all the prebuilt windows (like the Open File dialog, for example).

The designers of Visual C# decided that the days of sending large amounts of data to a printer were drawing to a close. They surmised that using data presentation schemes on a screen (written mostly in HTML) and a procedure for routing the screen presentation to a local printer would meet future hard copy needs. Hence, if you want to print from a local program, you must include the statement “using System.Drawing.Printing;” at the top of the source code file. The other common file that is required is “using System.IO” (input and output).

This using mechanism is really an invitation to third-party compiler writers who choose to create and market their own collections of classes — like classes that perform all the bookkeeping functions in a large industrial corporation or classes that account for large amounts of textual material for a publishing company. There are even classes of mathematical functions so NASA and other high-tech engineers can compute trajectories of satellites on a PC.

The largest collection of third-party software in this arena today is computer-aided design software that enables, for example, an architect to create large buildings in a single bound.

Each compiler manufacturer used to distribute wall-size posters that listed all the classes used in the compiler’s object-oriented structure. Today, however, due to the proliferation of classes, these posters are no longer used. Also, no one really cares how many classes are present within the confines of a compiler.

All that you care about, as a Visual C# compiler user, is that every executable statement that you enter into source code has a machine code backup list somewhere in the using files. If you type the name of an executable statement into Visual C# and support that statement with a using file that contains all the machine code, then your part of the compilation process is complete.

1.

If I add using statements (at the top of the source file) that are unnecessary, does this make the compiled project larger than it should be?

Answers

1.

No, not at all. What the compiler does is this: As it searches for the supporting machine code to support each executable statement, it will invariably enter the using files that are not involved in the necessary executables, and it wastes a little time, meaning a few milliseconds. Once all the useful machine code has been extracted from the read-only files and compilation moves to completion, all the using files are cast out. If your Visual C# compiler did not do this, the simplest project you can write would be several megabytes in size after compilation.

With the advent of OOP, each time a code + local variables packet was needed in the sequence of events in source code, that packet was created in real time with the “new” statement. For example:

Timer wxyz = new Timer();
wxyz.Interval = 1000;

When the first statement above is executed, the new instance of the Timer class is created (instantiated) by a constructor. Constructors would have been easy to write (by the compiler writers) except that the new languages such as Visual C# allowed multiple forms for classes like Timer. For lack of a better name, compiler writers called the simplest form of the class the basic form and called all the other forms overloaded forms.

Modern OOP compilers create (instantiate) just about every packet of code that appears in a project, but the compiler doesn’t force the programmer to do the repetitive instantiation. Instead, most of the recreating of these packets is done automatically and no human is involved. For example, every time an ANSIString is operated upon (to add or delete a character, change a character, or concatenate several strings into one string), the entire basic string is created new with every operation. All the old occurrences of the code are readied for the garbage collector, which clears the unused memory and makes it available to the operating system again.


Overloading

The best example of multiple forms for a single class is the StreamWriter class, a class that is used to write data to a file. In addition to the basic format, there are at least seven overloaded formats. (Note that the term “Stream” below means the file that is to be written to.)

  1. public StreamWriter(Stream); This is the basic format. Characters are written from a predetermined buffer with a fixed number of chars, in a predetermined character format (like ASCII), to the Stream.

  2. public StreamWriter(string); This form writes the data from a predetermined buffer in a predetermined format to a named ANSIString.

  3. public StreamWriter(Stream, Encoding); This format is the same as #1, but the programmer can name an encoding format other than ASCII.

  4. public StreamWriter(string, bool); This format is the same as #2 above, but it includes a switch that allows the writing to begin at the beginning of the string or at the end of the string (if any characters are already in the string). This is known as the append mode.

  5. public StreamWriter(Stream, Encoding, int); This format is the same as #3 above, but it includes a third argument, Int, which is the number of chars to be written to the Stream.

  6. public StreamWriter(string, bool, Encoding); This format is the same as #4 above, but it includes a third argument, Encoding, which specifies the format in which to write the data (other than the default ASCII format).

  7. public StreamWriter(string, bool, Encoding, int); This format is the same as #6 above, but it includes a fourth argument, Int, which is the number of chars to be written to the Stream.

  8. public StreamWriter(Stream, bool, Encoding, int); This format is the same as #5 above, but it includes a fourth argument to decide if the new block of data should be entered at the start of the Stream (overwriting all other data) or appended to the end of existing data.

In formats #3 and #5 the compiler will accept an alternate form:

public StreamWriter(Stream, ,Encoding, int);

In this case the blank between the first argument (Stream) and the second argument (Encoding) indicates a blank entry, which is unused here — a bool variable.

1. | Why didn’t the compiler writers just create eight forms of StreamWriter, named StreamWriter1 through StreamWriter8, and let it go at that? | Image from book
2. | How come the programmer did not have to create an instance of StreamWriter (using new) before the StreamWriter statement was used in the source code? | Image from book

Answers

1. | This Visual C# compiler is burdened with up to 800 MB worth of machine code in its library, and anything that could be done to diminish the size of the machine code package was welcome. By allowing this overloading, Microsoft was able to create one form of StreamWriter and set the branching points within this machine code to accommodate all the variations in StreamWriter that a programmer would ever need.
2. | The most common Visual C# statements are equipped with automatic constructors that perform the instantiation of their object(s) each time they are used. The less common objects (and Timer above is a good example of a little-used object) must be manually instantiated by the programmer.


Help

The helps in the v1.0 and 1.1 releases of Visual C# were difficult to use, but it appears that much improvement has been made in the v2.0 release. Hopefully, if a programmer is writing in the C# language, the only helps that he will see are the C# helps — not C, not C++, not Basic, not J#, etc.

To save time, the programmer must limit the scope of the search in these helps. Click on Help on the main menu and move immediately to the Edit Filters… item in the submenu. When the filter window opens in the middle of the IDE, pick .NET Compact Framework in the filter list at the top of the window. Immediately save this setting. (The helps won’t allow you to choose “Visual C#” as a filter at this time. If you do, the helps revert to “.NET Compact Framework.”)

Click on Help in the main menu again, then click on the Index submenu item. At the top right of the IDE you will find a Filtered By text box. Click on the down arrow and pick Visual C# in the list. Now your index search will be limited to C#, which is a subset of .NET Compact Framework.

In the master index there are so many categories for executable statements that you find yourself clicking on each one sequentially to find the information you need. From my viewpoint, all that a programmer needs is a list of the basic and overloaded forms of each executable statement in Visual C#, plus an indication of how to bring that state-ment’s machine code into play (which using statement will bring the code in). All the other stuff is peripheral information. Many of the executable statements have samples to show you how to use the statement (hooray!), but some do not, and you are left out in the cold.

Here are all the helps offered to you by the Help main menu item:

  1. Dynamic Help. These are code samples, getting started tips, explanations about Visual Studio .NET, etc. These are general helps.

  2. Contents. Introduction to Visual Studio .NET, getting assistance (product support), etc.

  3. Index. This is the big Kahuna! This alphabetical index for all statements in all Microsoft languages is a combo box: As you type characters into the text box the alphabetical list slides up and down to match the characters you have entered.

  4. Search. This is a search of everything in the Helps arena. This search is seldom useful if you have already done the Index search above.

  5. Index Results. Search Contents (item #2 above), search Index (item #3 above), or search Search (item #4 above). This adds nothing to your search; the combo box in #3 is still your best bet.

  6. Search Results. Same as #5.

  7. Edit Filters. The choice you enter here helps to limit the scope of any search you perform in the Index search (#3).

  8. Show Start Page. Exits help mode.

  9. Check for Updates. This is useful from time to time if your PC is online and can accept updates to the help texts.

  10. Technical Support. This is information on how to get paid technical support, third-party software, and Microsoft Developer Network (MSDN) help. Entrance into the MSDN via the Internet is free, but you have to register to use its helps. You will find Microsoft technical support very reasonable.

  11. Help on Help. A discussion on how to use the helps.

In summary, be sure to set the filters in Help and use the Index search to locate help on programming topics.


Chapter 8: Data Types and Declarations

Overview

This chapter discusses the data types you will use in Visual Studio C# and their declarations.

A Common Language Specification (CLS) now exists for C#, a specification initially written by Microsoft but approved by most international computer committees. The following table shows the CLS-compliant data types in Visual Studio C#:

byte | 0 minimum, 255 maximum; described in System.Byte.
short | -32,768 minimum, 32,767 maximum; described in System.Int16.
int | -2,147,483,648 minimum, 2,147,483,647 maximum; described in System.Int32.
long | -9,223,372,036,854,775,808 minimum, 9,223,372,036,854,775,807 maximum; described in System.Int64.
string | 0 minimum, no maximum; described in System.String.
char | 0 minimum, no maximum; described in System.Char. One byte (8 bits) if ASCII, two bytes (16 bits) if Unicode (which provides for characters in languages such as Japanese).
bool | Either true or false; described in System.Bool.
float | 32 bits — 23 bits for the number (7 digits), 8 bits for the exponent, and 1 sign bit; described in System.Single.
double | 64 bits — 52 bits for the number (15 to 16 digits), 11 bits for the exponent, and 1 sign bit; described in System.Double.
decimal | 102 bits — 96 bits for an integer number, 5 bits for a scaling factor, and 1 sign bit. For example, the integer number 1234567 with a scaling factor of 3 becomes 122334.567; described in System.Decimal.
Note

Since a decimal point may appear in both the decimal data type and the float/double data types, it may be necessary to mark a number with either an “m” for decimal (like the first letter in “money”) or an “f” for “float.” If a number is unmarked (and has a decimal point in it), the compiler will assume that the number is a double.

Here are some examples:

decimal decDollars = 101.67m;

If the letter “m” is removed from the end of 101.67, the compiler assumes the number is a double. Then, to place the number into a decimal named decDollars, the number is converted from a double to a decimal. With the “m” affixed to the number 101.67, the compiler knows that the number is a decimal.

double dblDollars = 101.7f;

The “f” at the end of the number confirms to the compiler that this is indeed a float number. However, if you remove the “f” (which stands for floating point), the compiler assumes that the number is a double anyway.

Since the double is the default, we recommend that you perform all floating-point arithmetic using double.

You will probably do 99.9 percent of your C# work using three data types: int, double, and string. If you are a finance person you will probably add a fourth, decimal.


Declaring a Number as a Constant

If a number is declared as a constant in a Visual Studio C# project, then the declaration must appear in the class declaration and the constant must be assigned a value in that declaration. Once a number is declared a constant, the number cannot be changed in the code.

public const double dblFearFactor = 7.22;


Naming Variables

We encourage you to use Hungarian notation when you declare variables. That is, the first two or three characters of the variable name indicate what type of variable it is. These two or three characters are usually in lowercase type. The variable names in this chapter use Hungarian notation.

int intNumber;
float fltTotalFees;
double dblInterestRate;

where “int” means integer, “flt” means float, and “dbl” means double.


Where to Place the Declarations

The new Microsoft MSBuild formats for all project files will be extended to include all the programming languages in the Visual Studio suite in the coming years. The most notable change in the project file formats is the decision to split the declarations for a given project into two groups. To accommodate this split, the new keyword “partial” is used, as shown in line CP010 below.

Some of the declarations (the computer-generated declarations that result from the controls and forms that the programmer chooses to include in the project) are placed in the *.Designer.cs window template file. As a result, this file will be rarely visited by the programmer (only the IDE writes to this file, except in rare circumstances).

Those additional declarations, which are the responsibility of the individual programmer, must be placed in one small area in the *.cs source code file. For example, in project CodePlacement, Form1.cs, all the programmer’s declarations are shown in lines CP011-CP013:

CP001:        #region Using directives
CP002-007:    [6 lines of 'using' statements]
CP008:        #endregion

CP009:        namespace CodePlacement
CP010:        partial class Form 1 : Form
              {
CP011:          public int intNumber = 6;
CP012:          int intGlobalNumber = 77;
CP013:          public int[] intArray = new int[] {1, 22, 88};

CP014:          public Form1()
                {
                  InitializeComponent();
                }

//-----------------------------------------------------------------------------------------//
CP101:          private void button1_Click(object sender, EventArgs e)
                etc.

The statements in public Form1() are all executable statements. No declarations will be tolerated here. This is where the programmer can place those executable statements that are commonly placed in the Load procedure (private void FormX_Load()) since public Form1() is executed before the window is opened (and never executed again).


Integer, Double, and Decimal Enumerations

A collection of integers, doubles, or decimals is declared as an integer, double, or decimal array in this manner (recall that every one of these declarations could be prefaced with either public or private, and perhaps the const declaration):

int[] intBunchOfIntegers = new int[4];            // Four integers.

or

double[] dblBunchOfDoubles = new double[6];      // Six doubles.

or

decimal[] decBunchOfDecimals = new decimal[9];   // Nine decimals.

To fill these arrays you add the enumerations at the end of the declaration:

int[] intBunchOfIntegers = new int[] {17, 3, 9, 88};

double[] dblBunchOfDoubles = new double[] {3.4, 3.1, 8.2, 6.6, 7.3, 0.8};

decimal[] decBunchOfDecimals = new decimal[] {1.1m, 2.2m, 3.3m, 4.4m, 5.5m, 5.6m,
           6.m, 7.m, 3.3m};

Note that the number of ints, doubles, or decimals need not be dimensioned if the enumerations are included because the compiler counts the number of entries and fills in the proper dimension.


Strings

The formal name for a string is an ANSIString (ANSI stands for American National Standards Institute). The string is so well supported in the Visual Studio C# compiler that there is no reason to ever use the old, alternate method of grouping characters together (the char string of the C language era, circa 1970). If you have ever used the char string, you know that it had to be terminated with the NULL character, \0, and its length had to be declared in advance (because early compilers had to allocate space in sequential memory for each string; hence each string length had to be declared at startup). Not so with the modern ANSI-String; each string is placed in floating memory and each time the string is modified in any way, a new floating location is established. The length of an ANSIString is contained in a separate construct; for example, the length of a string named strTotal is located in strTotal.Length.

There is one notable case where the char array appears in the Visual Studio C# compiler — when a file must be read in presized chunks of characters (statement ReadBlock()). In this case the data read from the file is placed in a character array and most programmers immediately transfer the data to a string in the next executable statement. For all practical purposes you will never use the char array again.

Case 1: An Individual String

The declaration is:

string strTotal;

or

private string strTotal;

To make this declaration global to the form, place it at the top of the class declaration. To make this declaration global to the form and allow this string to be used in other forms, the declaration is:

public string strTotal

Case 2: A Collection of Strings

If you declare a collection of strings, you must tell the compiler how many strings are in the collection:

string[] strBunchOfStrings = new string[33];

In this case there are 33 strings and each string has an individual length. The length of string #12 is strBunchOfStrings[11].Length because all counting starts at 0. Most texts call this collection of strings jagged arrays since each array has its own length. To make this collection usable by other forms you must place the word public in front and list it in the class declaration at the top of the *.cs source code file.


String Enumerations

There is no requirement that you both declare and fill a string at the time of its original declaration, but there is no reason not to do the complete task in one place:

string[] strBunchOfStrings = new string[] {"Somewhere", "Over the Rainbow",
          "Way up High", "There's a land that I've heard of"};

The operators used to manipulate the contents of strings are discussed in Chapter 10, “String Manipulation and Online Data Storage (Arrays).”


Chapter 9: Conversions between Data Types

Overview

Some computer programmers confront data conversions in the algorithms they create on an infrequent basis, but for most of us these conversions are the bane of our existence. In the past, the programmers who suffered the most were those who wrote financial programs. They performed all their mathematics in floating point (base 2 arithmetic), then converted the answers to base 10 arithmetic formats when it came time to print the results or display them on the screen.

Then the Visual Studio C# architects decided to include in their compiler the mathematics that most of us practice all our lives — decimal arithmetic (base 10). How sweet it is! Since there are now two arithmetic systems using decimal points (float and decimal), you will notice that numbers entered in Visual Studio C# algorithms often contain an “f” or an “m” at the end of the number to inform the compiler which type of data is present (float or decimal). The decision to use “m” as a decimal arithmetic identifier is especially appropriate since “money” begins with an “m.” But decimal number does not mean that the number always includes precisely two digits to the right of the decimal point — there can be as many as eight digits after the decimal point.

Here are some definitions that may be helpful in the conversion process.

Parse: A method to transfer data from a string into other, non-string data types (integer, float, double, etc.).

Convert: A method to convert data from non-string types primarily (but converts from string types occasionally).

Int16: A short.

Int32: An int.

Int64: A long.


Common Conversions

Listed below are the common data conversions you will encounter in programming with the Visual Studio C# compiler. The following examples are in executable form in project DataConversion at Visual Studio 2005\Projects\DemosSourceCode so you can confirm that the data conversions work as advertised.

  1. ANSIString to:

    1. Character string:

string strString = "Mary had a little lamb.";
charString = strString.ToCharArray();
// Do not attempt to terminate this 'charString' with the null char.
// The statement 'charString[strString.Length] = '\0'' won't work.

    2. Int32 (integer):

// 'strString' must be an integer! No float allowed here.
strString = "457";
int intNumber = Convert.ToInt32(strString);

      Example:

strTB1 = textBox1.Text; // (data taken from a text box)
string strShortYear = strTB1.Substring(2,2);
intYear = Convert.ToInt32(strShortYear);

    3. Int64 (long):

// 'strNumber' must be a long! No float allowed here.
string strNumber = "444444444";
long lgNumber = Convert.ToInt64(strNumber);

    4. Double (double-precision floating-point number):

strString = "433.8395";
double dblNumber = Double.Parse(strString);

      or

double dblNumber2 = Convert.ToDouble(strString);

    5. Decimal (base 10 arithmetic; two digits to right of decimal point):

strString = "445.33";
decimal decNumber = Decimal.Parse(strString);
MessageBox.Show("Item #1E-1 response is '" + decNumber.ToString() + "' .");

      or

decimal decNumber2 = Convert.ToDecimal(strString);

      Example:

dBuyNOS = Decimal.Parse(strNOS);
dBuyTP = Decimal.Parse(strTP);

  2. Character string to:

    1. ANSIString:

charString = "Mary had a little lamb".ToCharArray(); // Create an example string.
strString = new string(charString);

    2. Int32 (integer):

// 'charString' must be in integer format for 'Convert.ToInt32' to work. No float.
charString = "3376".ToCharArray();
strString = new string(charString);
// This next statement works ONLY with 'strString'. Won't work with 'charString'.
intNumber = Convert.ToInt32(strString);

      or

intNumber = Int32.Parse(strString); // Only 'strString' works -- no 'charString'.

    3. Int64 (long):

charString = "3394".ToCharArray();
// This 'strString' must be in integer format. No float.
strString = new string(charString);
lgNumber = Convert.ToInt64(strString);

      or

lgNumber = Int64.Parse(strString); // Only 'strString' works -- no 'charString'.

    4. Double (double-precision floating-point number):

strString = "1935.006";
charString = strString.ToCharArray();
// charString[strString.Length] = '\0'; Not needed. Terminates the program.
strString = new string(charString);
dblNumber = Convert.ToDouble(strString);

    5. Decimal (base 10 arithmetic; two digits to right of decimal point):

strString = "144.144";
charString = strString.ToCharArray();
//charString[strString.Length] = '\0'; Not needed. Terminates the program.
strString = new string(charString);
decNumber = Convert.ToDecimal(strString);

  3. Int32 (integer) to:

    1. ANSIString:

intNumber = 565758;
strString = Convert.ToString(intNumber);

      Example:

string strYearTemp = Convert.ToString(iYear);
string strWeekTemp = Convert.ToString(iWeek);

    2. Character string:

intNumber = 343536;
strString = intNumber.ToString();
charString = strString.ToCharArray();

    3. Int64:

intNumber = 7788;
lgNumber = (long) intNumber;

    4. Double (double-precision floating-point number):

intNumber = 345;
double dblNumber9 = Convert.ToDouble(intNumber);

    5. Decimal (base 10 arithmetic; two digits to right of decimal point):

intNumber = 722;
decimal decNumber17 = Convert.ToDecimal(intNumber);

  4. Int64 (long) to:

    1. ANSIString:

lgNumber = 247902;
strNumber = lgNumber.ToString();

    2. Character string:

lgNumber = 1356992;
strNumber = lgNumber.ToString();
charString = strNumber.ToCharArray();

    3. Int32:

lgNumber = 456744;
intNumber = (int) lgNumber;

    4. Double (double-precision floating-point number):

lgNumber = 3478902;
double dblNumber14 = (double) lgNumber;

    5. Decimal (base 10 arithmetic; two digits to right of decimal point):

long lgNumber3 = 353552;
decimal decNumber4 = Convert.ToDecimal(lgNumber3);

  5. Double (double-precision floating-point number) to:

    1. ANSIString:

dblNumber = 123456789;
strString = Convert.ToString(dblNumber);

    2. Character string:

dblNumber = 987654321;
strString = Convert.ToString(dblNumber);
charString = strString.ToCharArray();

    3. Int32:

double dblStart = 74738.22d;
int intFinish = (int)dblStart;

    4. Int64:

double dblNow = 834.228;
long lgLater = (long)dblNow;

    5. Decimal (base 10 arithmetic; two digits to right of decimal point):

dblNumber2 = 77.33456d;
decNumber = Convert.ToDecimal(dblNumber2);
// This conversion (5E) does not currently work!

  6. Decimal (base 10 arithmetic; two digits to right of decimal point) to:

    1. ANSIString:

decNumber = 1024.67m;
strString = decNumber.ToString();

      Example:

// Convert all numbers to 'string' and write them on Form2.
strPricePerShare = dPricePerShare.ToString();
strNetNOS = dNetNOS.ToString();

    2. Character string:

decNumber = 677.34m;
strString = decNumber.ToString();
charString = strString.ToCharArray();
// charString[strString.Length] = '\0'; Not needed.

    3. Int32:

decimal decRowboat = 1375.22m;
int intMerrily = Convert.ToInt32(decRowboat);

      or

int intMerrily2 = decRowboat.ToInt32(); // This won't work.

    4. Int64:

decimal decRowboat2 = 136.207m;
// long lgMerrily3 = decRowboat2.ToInt64(); This scheme won't work!

      or

long lgMerrily4 = Convert.ToInt64(decRowboat2); // This works.

    5. Double (double-precision floating-point number):

decimal decFlow = 3555.35m;
//double dblGently = decFlow.ToDouble(); Won't work.

      or

double dblGently2 = Convert.ToDouble(decFlow); // This works.

  7. Char string to integer example:

charString = "3376.44".ToCharArray();
// Convert to a true number, like 'double'.
strString = new string(charString);             // Convert char to string.
dblNumber = Convert.ToDouble(strString);        // Convert string to double.
// Round the 'double' to NO digits after the decimal point.
Math.Round(dblNumber,0);                        // Round off the double number.
// Cast the 'double' to an 'integer'.
int intAnswer = (int)dblNumber;

1.

With FileStream (which only reads bytes of data), is
      there a way I can read blocks of data?

Answers

1.

Yes, once FileStream has opened a file, you can read
      bytes of data and convert them to chars and later strings. Example:

  // Begin file opening with 'FileStream'.
  FileStream fs;
try
{
     fs = new FileStream("KTWeeklyNetWorth.dta", FileMode.OpenOrCreate,
                                                 FileAccess.ReadWrite);
     // Read from the file.
     byte[] byteBlockData = new byte[47];
     char[] charBlockData = new char[47];
     fs.Read(byteBlockData,0,47);
     // Convert 'byte' to 'char'.
     for(int mm = 0; mm < 47; mm++)
     charBlockData[mm] = Convert.ToChar(byteBlockData[mm]);
     string strBlockData = new string(charBlockData);
     MessageBox.Show("BlockData read is '" + strBlockData + "' .");
}
catch
{
     MessageBox.Show("Cannot open file 'KTWeeklyNetWorth.dta'.");
       return;
}

If you examine all the conversions shown above, you must conclude
that the days of working with character strings are just about over. The
ANSIString has every attribute that a character string has, and it keeps track
of its length in a subitem named .Length. Below is a list of the queries that
can be performed on an ANSIString (a string). Each of these queries returns a
bool response, true or false:

  •

Char.IsControl(string str, int Index);

    Is the character named in Index a control character?

Char.IsSeparator(string str, int Index);

  •

    Is the character named in Index a separator character?
  (Used in database applications only.)

Char.IsWhiteSpace(string str, int Index);

  •

    Is the character named in Index a blank space?

Char.IsPunctuation(string str, int Index);

  •

    Is the character a comma, colon, semicolon, period,
  etc.?

Char.IsSymbol(string str, int Index);

  •

    Is the character a symbol (such as the Greek letter
  alpha)?

Char.IsDigit(string str, int Index);

  •

    Is the character between 0 and 9?

Char.IsNumber(string str, int Index);

  •

    Is the character between 0 and 9?

Char.IsLetter(string str, int Index);

  •

    Is the character between A and Z or a and z?

Char.IsUpper(string str, int Index);

  •

    Is the character uppercase?

Char.IsLower(string str, int Index);

  •

    Is the character lowercase?

Char.IsLetterOrDigit(string str, int Index);

  •

    Is the character a letter or a digit?

For each of the above there is an alternative formulation. For
example:

if(IsDigit(strTB[5])) [One argument].......

is the same as:

if(IsDigit(strTB, 5)) [Two arguments]


Project DataConversion

Execute project DataConversion at Visual Studio 2005\Projects\DemosSourceCode so you can confirm that the data conversions listed here work as advertised.


Chapter 10: String Manipulation and Online Data Storage (Arrays)

String Manipulation

All modern compilers have departed from the old 1960s and 1970s “character string” concepts that required that each char string be dimensioned at the top of the program in the declarations section (to provide proper storage space on the hard drive) and the char string, when placed in this storage location, be terminated with the NULL character — the \0 character.

All of the above has been replaced by the ANSIString. The ANSIString uses no termination character. So if there is no null character at the end of the string, how does the compiler know where the string ends?

The answer is simple: For every ANSIString, Visual Studio C# creates a structure with two elements: The first element contains the string itself, and the second element is an integer that notes the length of the string. How simple, and yet so effective. For example, if a string is named strTime, then its length exists and may be found in strTime.Length.

These are the common tasks performed on ANSIStrings:

  • Create a string.

  • Replace one or more characters in the existing string with new characters.

  • Remove characters.

  • Insert characters.

  • Concatenate two or more strings together.

To create a string named newString:

str newString = "Now is the time for all good men to come to the aid of their country."

The Replace statement is a global replacement of one character by another character:

newString = newString.Replace('m', 'x'); // Replace all occurrences of 'm' with 'x'.

The resulting string is: “Now is the tixe for all good xen to coxe to the aid of their country.”

Note that the single character is enclosed by single quotes this time (not double quotes). Replace won’t work for strings with multiple characters; you cannot do this:

strBFOS[33] = strBFOS[33].Replace('wow', 'gee'); // Won't work!

To remove characters:

str newString = "Now is the time for all good men to come to the aid of their country."
newString = newString.Remove(7,4); // Remove 4 chars, beginning at position 7. The first
                                   // character position is zero.

The resulting string is: “Now is time for all good men to come to the aid of their country.”

Here is how you can insert characters:

str newString = "Now is the time for all good men to come to the aid of their country."
newString = newString.Insert(28,"y"); // Note the double quotes, not single quotes around
                                      // the letter y.

The resulting string is: “Now is the time for all goody men to come to the aid of their country.”

You can also perform the remove and insert operations in one statement:

str newString = "Now is the time for all good men to come to the aid of their country."
newString = newString.Remove(29,3).Insert(29,"women"); // Remove 'men' and insert 'women'.

The resulting string is: “Now is the time for all good women to come to the aid of their country.”

When the C language was first proposed by Bell Laboratories, there was no concatenation scheme included in the package. To combine two strings, the programmer was expected to create a char array with the proper number of characters to hold both the first and the second string, plus one space for the NULL character. Then the characters were transferred from their original strings to the new, larger string, one character at a time. Thank goodness the for loop existed at that time!

Concatenation is easy with Visual Studio C#:

str newString = "Now is the time";
str newString2 = " for all good people to come to the aid of the party.";
newString = newString + newString2;

Alternative method:

str newString = "Now is the time";
newString = newString + " for all good people to come to the aid of the party.";

The resulting string for both methods is: “Now is the time for all good people to come to the aid of the party.”


Online Data Storage (Arrays)

At one time there were entire college computer science courses devoted to the study of creating new ways to make lists in a computer. There were linked lists, multiply-linked lists, circular lists, and on and on. Every time a prototype of a new type of list was announced in the mathematical and computational journals, anxious graduate students would turn out reams of proposals to study how this new list could be loaded, fetched, searched, and sorted. There were studies reported in national journals to prove which list methods were superior.

Then the database industry moved into the fray, and developed turn-key systems that would store more data than man had ever conceived of in the last thousand years. Built into these turnkey systems were algorithms that loaded, fetched, searched, and sorted data (like the huge systems that track airline reservations, for example, or the national telephone book).

Meanwhile, all that the average computer programmer wanted was a simple and convenient way to store intermediate data that his or her algorithm could process in real time to produce reasonable outputs. After 40 years of innovation, nothing has been found to serve the average computer application better than the simple array, be it one-dimensional, two-dimensional, or even three-dimensional.

Visual Studio C# made life easy to account for multiple dimensioned arrays with the invention of the jagged array. The jagged array is a group of ANSIStrings that are grouped together for ease of fetch, but each ANSIString has its own length. Recall that each ANSIString is a structure with two elements: the string text and the string length.

For the individual ANSIString there is no declared dimension. The string computes its own length and puts that number into ANSIString-Name.Length. If you have a collection of 10 strings you can provide space for them in memory by giving them all the same name, such as ANSIStringCollection[0] through ANSIStringCollection[9]. The declaration for such a collection of ANSIStrings is:

string[] ANSIStringCollection = new string[10];

Remember that this is a declaration for a two-dimensional string array, and one of the dimensions is unexpressed — only the individual string knows its length.

If you require a three-dimensional string array, the declaration is:

string[ , ] ANSIStringWad = new string[10, 20];
Note

These rules do not apply to groups of integers, floats, doubles, etc., because these groups are not jagged. Each item in the array occupies the same number of bytes in memory. For example, this declaration:

int[ , ] intWadOfNumbers = new int[10, 20];

means exactly the same thing it meant in C and C++: There are 10 groups of 20 integers each (and each integer fills the same number of bytes in memory).


A Sharp Delineation between Char and ANSIString

There are a few occurrences of Visual Studio C# that force the programmer to accept data in a character (char) format (as opposed to an ANSIString format). For example, the principal file reader statement StreamReader.ReadBlock places the data it reads from the file into a char array, not an ANSIString. Then the programmer immediately places the char data into an ANSIString array and carries on (see line SU0630 below). It is not clear why this limitation was imposed on the StreamReader construct.

             // Read a record from 'Masterfile.dta'.
SU0626:      string[] strData = new string[2];

             // Begin reading the file.
SU0627:      w.ReadBlock(charRealBlock, 0, 38);
SU0628:      ItemNo++;
SU0629:      strData[0] = ItemNo.ToString();
SU0630:      strData[1] = new string(charRealBlock, 0, 38); // Convert char string to
                                                            // ANSIString

The ANSIString data type is highly sophisticated. There is a substring procedure that picks off any portion of a larger string and places it into another new string — a wonderful aid in parsing, searching, and sorting. For example, line SU4025 below picks off nine characters in strTemp beginning at char 24 (where the first char in the string is char 0) and places them in string strSubStr. This is powerful programming. In line SU4026 these nine characters are compared with the nine characters in strTB3. There are no NULL characters in either string.

SU4023:     vv.ReadBlock(charRealBlock, 0, 38);
SU4024:     strTemp = new string(charRealBlock); // Convert char string to ANSIString

            // 'strTemp' contains 42 chars because the 'new string' process copies
            // all sets of '\\' to the new string.
SU4025:     strSubStr = strTemp.Substring(24,9); // 20 chars plus 4 extra '\' chars.
SU4026:     if(strSubStr == strTB3)
SU4027:     {
SU4028:       MessageBox.Show("Match Filename " + strTB3 + " found in record #"
                               + (zz+1).ToString() + " .");
SU4029:       strTB4 = strTB3;
SU4030:       RecordFound++;
SU4031:     }
SU4032:     else

From time to time, you may need the single-subscripted character array. The declaration looks like this:

char[] charRealB = char[38];

where the name of the char array is charRealB. The char array can be reused over and over (that is, it need not be recreated each time you use it). You can place characters in the array to overwrite any characters that need rework, etc.

You will seldom need a double-subscripted character array (an array of arrays) in modern coding, but this is its declaration:

char[ , ] charDoubleD = char[16,22];

With the advent of Visual Studio C# there is no need for your program to use character arrays. Learn to use the ANSIString arrays — the power is yours.


Multiple-Dimension Arrays

You will continuously use two-dimensional string arrays. However, because they are jagged, they are never reusable. Anytime you alter a string array you must create a new copy of that string. This constant recreation of variables using the “new” designation (which creates a new instance of the basic object) is necessary in object-oriented programming, and it also creates a large number of code snippets whose usefulness exists for only a few fleeting milliseconds. Thus, it was necessary to invent a garbage collection scheme and get this superseded code out of the project (primarily to save space in memory). The garbage collection scheme is implemented in each FormX.Designer.cs file:

// Clean up any resources being used.
protected override void Dispose(bool disposing)
{
  if (disposing && (components != null))
  {
    components.Dispose();
  }
  base.Dispose(disposing);
}

Sometimes programmers condense this sequence into:

protected override void Dispose(bool disposing)
{ if (disposing && (components != null)) components.Dispose();
  base.Dispose(disposing);
}

because old programmers still think in terms of assembler programming where every line was valuable, and they never wasted space.


Array Initialization

To initialize a jagged array, you enter the arrays just as you always have in the past, as shown in line PL0302 below.

PL0300:      void OnPrintPage(object obj, PrintPageEventArgs ppea)
PL0301:      { // Create the 11 records at the top of the printed page.

PL0302:      string[] strRT = new string[] {"KT Weekly Net Worth", "Item No", "Year-Week",
               "Total Worth", "Total Cost", "Gain / Loss","----------","---------------",
               "---------------","--------------","---------------"};

This is how you initialize integer arrays:

PL0303:      int[] Base6 = new int[] {1270,0,800,1600,2400,3200,0,800,1600,2400,3200};
PL0304:      int[]Y6 = new int[] {160,350,350,350,350,350,460,460,460,460,460};
PL0305:      int[] Data6 = new int[] {100,910,1650,2400,3300};

The compiler counts the number of items in the curly brackets and sets this number as the dimension of the integer array. A string of strings (PL0302) need not be dimensioned if it is going to be initialized in the declaration — the compiler counts the number of entries in the enumeration of the strings and sets that dimension.

To initialize a two-dimensional integer array we recommend that you proceed carefully:

Int[ , ] TwoDArray = new[2,6] {{3,4,5,6,8,12} , {-2,16,77,4,8,8}};

In the case shown above, the string and integer arrays are declared locally in a procedure named OnPrintPage. If other procedures within one form need these dimensions, the declarations must be moved to the top of the FormX.cs source code file and characterized as private. If the numbers are required in other windows, they must be characterized as public and each window that needs the numbers must be sent a handle to reach the numbers (see Chapter 5).


ToUpper() and ToLower()

These perennial favorites are also contained in Visual Studio C#. The statements are:

strName = strName.ToLower();

and

strName = strName.ToUpper();

Even though the word new does not appear in these statements, a new copy of the string is created each time and the old copy is left for eventual pickup by the garbage collector.


The Double Backslash

For those who detest placing two backslashes in a string to show that one backslash is indeed required in the string, for example:

strFilename = new string("C:\\Program Files\\KT\\Masterfile.txt");

You can show the compiler that the backslashes are a part of the string (not control characters) by using the @ character:

strFilename = new string(@"C:\Program Files\KT\Masterfile.txt");

Note that the @ is in front of the string, not inside the string.


Chapter 11: Branching and Looping

Overview

Computer programming cannot perform its assigned tasks without if logic statements (branching statements) and loops (repeated code). In the Visual Studio C# compiler there is one style of if statement and four loop sequences:

  • The for loop loops a prescribed number of times (unless a decision is made to jump out of the loop early).

  • The foreach loop is similar to the for loop except that the number of times the loop executes is set by some outside variable.

  • The while loop continues to loop until some test is met. This loop is used primarily to find a particular code sequence in a collection of arrays or files.

  • The do…while loop makes the test for continuance at the bottom of the loop rather than at the top.

After the for loop discussion, the goto statement is briefly discussed as an alternative to the break statement. The switch…case construct is shown at the end of the chapter.


Visual Studio C# Equality/Inequality Symbols

These are the equality/inequality symbols used in Visual Studio C#:

Equal to | ==
Not equal to | !=
Less than | <
Greater than | >
Less than or equal to | <=
Greater than or equal to | >=


The if Statement

The if statement was originally called a branching statement, meaning that a decision had to be made so the program could branch one way or the other. There are only two branches for the if statement. If more than two branches are required, most programmers turn to the switch…case statement (which is discussed at the end of this chapter). The switch…case statement can branch any number of ways.

This is the if statement formulation:

if( /*Statement that can be either true or false*/)
{
  If 'true', then execute these statements.
}
else
{
  If 'false', then execute these statements.
}

The else portion of the statement is optional. The statement within the if statement can be a compound statement:

// IA030:
if((strTarget == 'AA') && (intShotNumber < 7))
MessageBox.Show("Msg #3: Bull's Eye!");
else
MessageBox.Show("Msg #4: Fix bayonets.");

In a compound statement, both boolean statements must be true if the complete statement is true. If either one or both of the boolean statements is false, then the complete statement is false. This will be demonstrated in the demonstration program IfAndLoop that you will be asked to run at the end of the chapter.

Many for loops contain an if statement inside the loop, and it is the if statement that determines whether the focus should remain with the loop or break out and process the code that follows the loop.


The for Loop

The for loop looks like this:

// IA040:
                  int[] intArrayNumbers[] = new int[] {12, 43, 77, 88, 123, 4, 77, 11, 13, 77,
                                             4, 33};
                  int intReferenceNumber = 77;
// IA042:         int intLocation = -1;

// IA043:         for(int kk=0;kk<12;kk++)
                  {
// IA045:           if(intArrayNumbers[kk] == intReferenceNumber)
                    {
                      intLocation = kk;
                      // break;
                    }
// IA050:         }

// IA051:         if(intLocation = -1) // Reference Number not found in 'Array Numbers'.
                    MessageBox.Show("Msg#5: Reference Number " + intReferenceNumber +
                    " not found in ArrayNumbers[] .");
                  else // Number found.
                    MessageBox.Show("Msg#6: Reference Number " + intReferenceNumber +
                    " found at location 'ArrayNumbers[" + intLocation.ToString() + "] .");

Line IA042 is necessary to capture the location of ArrayNumbers[] if the number in ArrayNumbers equals intReferenceNumber. It is set to -1 so line IA051 can detect if the statement in IA045 is ever found to be true. The number -1 is a safe number here because the numbers of the ArrayNumbers[] elements will be 0 through 11.

Line IA043 defines the for loop. Initially, kk is set to 0, and the loop executes if kk is less than 12. The iterator, kk++, is not executed until the end of the loop (after IA050).

Line IA045 checks to see if ArrayNumbers[0] is equal to intReferenceNumber, which equals 77 in this case. If true, then location 0 is placed into intLocation. Then the iterator kk is increased to 1 and the test in line IA045 is repeated. No matter what happens, this test is performed 12 times, from kk = 0 to kk = 11. At the end of 12 repetitions kk is set to 12 for the thirteenth repetition, but the test kk < 12 in line IA043 fails and the loop stops.

Line IA051 determines if any of the ArrayNumbers[] were equal to 77 in this case. If true, then the location of that number is placed in intLocation.

1. | What if three of the 12 numbers in ArrayNumbers[] were 77?
2. | How can I quit the process the first time that line IA045 is satisfied (true)?
3. | What does break mean?

Answers

1.

Line IA045 would place the locations in intLocation three times, but only the last entry would appear in intLocation when the loop ended.

2.

Lines IA042-045 are modified with lines IA046-049:

IA042:     int intLocation = -1;

IA043:     for(int kk = 0 ; kk < 12 ; kk++)
IA044:     {
IA045:       if(intArrayNumbers[kk] == intReferenceNumber)
IA046:       {
IA047:         intLocation = kk;
IA048:         break;
IA049:       }
IA050:     }

3.

When the if statement in line IA045 is satisfied, then the break statement in line IA048 is executed. The break statement causes the program to exit from the if statement in line IA045 and the for statement in line IA043. So the next line executed is the one below IA050.


The goto Statement as a Replacement for the break Statement

An alternative formulation for the break statement is the infamous goto statement. The Visual Studio C# compiler will accept a goto statement, despite the fact that some snooty programmers consider the use of the statement to be vulgar and uneducated. All we can say is, it does what it is supposed to do. Replace “break” with “goto MAUI” and at line IA050, type MAUI:. Now the code breaks away from this for loop as soon as the first case of 77 is found. The final form looks like this:

IA043:     for(int kk = 0 ; kk < 12 ; kk++)
IA044:     {
IA045:       if(intArrayNumbers[kk] == intReferenceNumber)
             {
IA047:         intLocation = kk;
IA048:         goto MAUI;
             }
           }

IA050B:    MAUI:

This next example is a common occurrence with for statements. The number of loops the loop will make is not hard coded into the loop but is determined just before the loop is entered (in this case, a statement intEndLineNumber = 2):

IA042:   int intLocation = -1;
IA043:   for(int kk=0;kk<intEndLineNumber ; kk++)
IA044:   {
IA045:     if(intArrayNumbers[kk] == intReferenceNumber) intLocation = kk;
         }

Note that intReferenceNumber comes from code above this snippet, and it equals 77 this time.

This loop will execute twice — once when kk = 0 and once when kk = 1 (since intEndLineNumber equals 2).

For the next example, intEndLineNumber comes from above as 0. What happens now?

IA042:   int intLocation = -1;

IA043:   for(int tt=0;tt<intEndLineNumber ; tt++)
         {
IA045:     if(intArrayNumbers[tt] == intReferenceNumber) intLocation = tt;
         }

The loop will not execute since 0 is not less than 0 the first time the test “tt < intEndLineNumber” is made in line IA043.

If the text “tt < intEndLineNumber” were changed to “tt <= intEndLineNumber” (tt less than or equal to intEndLineNumber), the loop would execute because 0 is less than or equal to 0 the first time through the loop.


The foreach Loop

The foreach loop is a new loop added specifically for the Visual Studio C# compiler. Any foreach loop that you construct can also be created with a for loop using a slightly different formulation. The foreach loop looks like this:

Int[] intArray = new int[] {2, 4, 6, 8, 10, 12, 2, 4, 6, 2, 4, 6};
int intTotals = 0;
int intNumber = 2;
foreach(int intNumber in intArray) intTotals++;

This code would produce the answer intTotals = 3.

The alternative formulation is:

Int[] intArray = new int[] {2, 4, 6, 8, 10, 12, 2, 4, 6, 2, 4, 6};
int intTotals = 0;
int intNumber = 2;
for(int kk = 0; kk < intArray.Length; kk++)
  if(intArray[kk] == intNumber) intTotals++;

This code would also produce the answer intTotals = 3.

According to the Microsoft literature, a foreach loop is much faster than a for + if construct.


The while Loop

The while loop differs from the for loop in that the loop is executed an indefinite number of times. The purpose of lines IA064-IA069 below is to find out if there is a blank character in strFileName. The while loop looks like this:

IA060:        string strFileName1 = "BullWinkle";
IA061:        int intFirstBlankCharLocation = -1;
IA062:        int ww = 0;
IA063:        while(strFileName1[ww] != ' ') ww++;
IA064:        if(ww+1 == strFileName1.Length)
IA065:        MessageBox.Show("Msg#7: There are no blank spaces in this filename.");
IA066:        else
IA067:        {
IA068:          intFirstBlankCharLocation = ww;
IA069:          MessageBox.Show("Msg#8: There is a blank space in this filename"
                  + " at location '" + intFirstBlankCharLocation.ToString() + "' .");
IA070:        }

There is something terribly wrong with this code snippet, however. What if there are no blanks in this string? What happens? The code in line IA063 faults and stops as soon as ww exceeds the length of strFileName. This is demonstrated in the demo program IfAndLoop.

A better formulation would be to change line IA063 to:

IA063: while((strFilename1[ww] != & &) && (ww < strFileName1.Length)) ww++;

The compound test in IA063 — ((strFilename1[ww] != ' ') && (ww < strFileName1.Length)) — becomes false as soon as either one or both of the substatements becomes false.


The do…while Loop

The do…while loop makes the test for continuance at the bottom of the loop, rather than at the top. It looks like this:

int intCharPointer = 0;
Filename.length = 4;

do
{
  // Do something.
  intCharPointer++; // Increment the CharPointer.
}
while(intCharPointer < Filename.Length);

In this case, Filename is a string with four characters in it. Its char positions are numbered 0, 1, 2, and 3. The first time through the loop, char 0 is checked for something and intCharPointer is incremented to 1. The loop loops again and checks char 1; intCharPointer is incremented to 2. The loop loops again and checks char 2; intCharPointer is incremented to 3. The loop loops again and checks char 3; intCharPointer is incremented to 4, and the while test at the bottom fails.


The switch…case Statement

The switch…case statement is the construct of choice if the program must branch more than two ways at a point in the source code. For only two branches, the if statement works well. The switch…case construct works like this:

int intNumber = 2;

switch( intNumber)
{
  case 0:
    // Do something.
    break;
  case 1:
    // Do something.
    break;
  case 2:
    // Do something.
    break;
  case 3:
    // Do something.
    break;
  default:
    // Do something.
}

The default case is optional.


Project IfAndLoop

To review the if, for, and while statements, move to the PC desktop, open the IDE, pick File on the main menu followed by Open | Project/Solution, and maneuver to Visual Studio 2005\Projects\DemosSource-Code\IfAndLoop. Open this folder and click on the highlighted file IfAndLoop.sln to place the project into the IDE. Perform the exercises in the Form1.cs source code file, using the Button1_Click procedure.


Chapter 12: Arithmetic

Overview

The scientists and engineers who created the first digital computers wanted machines that could compute mathematical answers quickly and accurately. For this reason, a measure of merit to rank early machines was created by these same scientists and engineers: How quickly can a given machine add two numbers together and place the answer in memory? These were not integers (that calculation was too simple). The numbers were floating-point, or as we call them today, floats (base 2 arithmetic). Most manufacturers discovered early that floating-point arithmetic would require a very large word size, on the order of 64, 128, or even 256 bits. So even the earliest CPUs were equipped with methods to concatenate groups of smaller size words. While some early machines claimed speed as their forte, others claimed accuracy. The smaller the total word size, the faster the machine could add two numbers. The larger the total word size, the more accurate the answers became.

Since three bits can contain only eight different numbers, it took four bits (16 numbers) to contain one base 10 number. A typical 32-bit word used for arithmetic purposes reserved 24 places for the mantissa (the number written in base 2) and only eight places for the exponent. This layout for floating-point numbers allowed a representation of numbers from -3.4 x E38 (10 to the 38th power) to +3.4 x E38. But the 24-place mantissa in base 2 only created about six digits of accuracy (in base 10). The IEEE Standards Committee immediately declared that a double data type should be included in computers, with a 53-bit mantissa and an 11-bit exponent. With the double the mantissa size went from 24 to 53 bits, and the number of digits of accuracy (in base 10) went from 6 or 7 to 15 or 16. The double serves 99 percent of the world for accuracy purposes today.

For those who wanted even greater accuracy (like NASA and astronomers), machines like the Cray were built with a basic arithmetic word size of 128 or 256 bytes.


Arithmetic Symbols

The only arithmetic symbols that have changed since their inception in the 1960s are the exponentiation and logarithmic symbols. The following table shows these symbols.

Addition | intAnswer = intA + intB;
Subtraction | decAnswer = decA - decB;
Multiplication | dblAnswer = dblA * dblB;
Division | dblAnswer = dblA / dblB;
Exponentiation | dblAnswer = Math.Pow(dblA, 3.474); // dblA to the 3.474 power
Logarithms base 10 | dblAnswer = Math.Log10(dblA);
Logarithms base e | dblAnswer = Math.Log(dblA);

Parentheses are used to enclose all mathematical expressions:

double dblAnswer = (dblA / dblB) * dblC + dblD;
Note

You would never have a mathematical expression with a variable type declared on the right-hand side of the equation. This can never occur:

double dblAnswer6 = (double dblA2 / double dblB2) + 4.2;

In this case, both dblA2 and dblB2 must be declared before this statement.

Also, both dblA2 and dblB2 must have been filled with numbers before this statement can be executed. The Visual Studio C# compiler checks that the variables are filled before any arithmetic can be accomplished using them.

This sequence is acceptable:

double dblA2 = 4.0;
double dblB2 = 3.5;
double dblAnswer6 = (dblA2 / dblB2) + 4.2; // '4.2' automatically becomes a 'double'.

The conversions between the various arithmetic data types are shown in Chapter 9, “Conversions between Data Types.”


Project Arithmetic

We have constructed a project named Arithmetic in the group of demo projects (the ones that you downloaded to your PC from www.wordware.com/unlockingcsharp and placed into C:\Documents and Settings\Owner\My Documents\Visual Studio 2005\Projects\DemosSourceCode).

Open the IDE and pick File on the main menu, followed by Open and then Project/Solution. When the Open Project window appears, move to Visual Studio 2005\Projects\DemosSourceCode\Arithmetic and double-click on project Arithmetic to bring it into the IDE. When the list of files in Arithmetic appears, click on the highlighted file, Arithmetic.sln, which is the entry point into the project.

Open the Form1.cs source code file. Within the button1_Click procedure there are many arithmetic expressions. Some are ready to run; others need your inputs to compile and run correctly.


Chapter 13: Message Boxes

Overview

We mentioned in Chapter 2 that users have two principal means of communicating with their computer: the keyboard and the mouse. But how does a computer application communicate with the user? The answer was conceived early in the development of computer software protocols: the message box.

In the days before Windows, the message returned by an application was a couple of lines that appeared on the blackened DOS screen, preceded by a word or words to wake up the user, like “DANGER!” or “ERROR IN FILE FETCH!”. But those couple of lines were important because they warned the user that something was wrong inside the application. With the advent of Windows, the message box immediately occupied a spot in the center of the screen, and it could appear as small or as large as the computer programmer wanted.

It wasn’t until the early 1990s that message boxes became truly sophisticated — not only did they warn the user of impending doom, but in some cases they asked the user what should be done to alleviate the problem. This causes one to surmise that the next generation of message boxes will not only appear on the user’s screen, but their messages will emanate from the speakers on either side of the display. “DOOM, DOOM, DOOM, I SAY,” said the speakers as they burst into life. “YOU HAVE INTRODUCED SO MUCH TRASH INTO MY DATABASE THAT I AM UNABLE TO CARRY ON.” (That sounds like HAL from 2001: A Space Odyssey.)

Meanwhile, totally apart from the message box gurus, a significant number of compiler writers were constantly reinventing software that would debug an ailing software program. You will find that the debug schemes offered by Visual Studio C# are extraordinarily good; they embed their own query code into the code that the programmer is writing. When debugging is needed, the debuggers are ready and willing to take charge!

But the one thing these compiler writers had not counted on was the tenacity of the average programmer who wanted to solve her own problems in her own ways. (Programmers just don’t like formal debuggers.)

Then along came the modern message box, and many programmers saw, in that message box, a way to circumvent all the smarts of debuggers. Each programmer became a debugger in his own right, and the tool he used to peer into his software to debug was the message box.

For example, most programmers, as soon as they finish a coding sequence to open and read a file, place a message box at the end of the sequence to read back to the screen what was just read from the file. When the source code approached completion, the cross-weave of message boxes was commented out of the code. When it came time to release the code to a customer the message boxes were removed completely.

Today, you will find that the Visual Studio C# message box is one of the most complete pieces of software you will ever have occasion to use. There are two forms: one that displays a message only and one that allows a decision.

The first does not require the user to make a decision. It is for information only.

MessageBox.Show("Record " + strRecordName + " placed in file " + strFileName + " .");

This would produce a message box with a statement in the center: Record 499L placed in file 24Feb04Ultra.

The second form requires the user to make a decision.

if(MatchFound > 0)
{
  DialogResult dr = MessageBox.Show("Duplicate entry found for year-week '"
    + strYW + "' . \n Old Entry is: '" + strDDTData + "' ."
    + "\n New Entry is: '" + strMasterString
    + "' .\n\nDestroy 'Old' entry?", "Duplicate",
    MessageBoxButtons.YesNo, MessageBoxIcon.Exclamation);

  if(dr == DialogResult.Yes) // Replace old entry with new entry.
  { etc.

As MessageBox shows:

  1. The first argument is a statement printed in the center of the message box:

"Duplicate entry found for year-week '" + strYW + "' . \n Old Entry is: '" +
           strDDTData + "' ." + "\n New Entry is: '" + strMasterString +
           "' .\n\nDestroy 'Old' entry?"

    where \n means “skip to the next line on the screen.”

  2. The second argument is a caption for the message box:

"Duplicate"

  3. The third argument specifies which buttons should be presented to the user. In this case there are two buttons, Yes and No:

MessageBoxButtons.YesNo

  4. The fourth argument chooses an icon that appears at the top left of the message box:

MessageBoxIcon.Exclamation

  5. A fifth argument exists, if needed:

MessageBoxDefaultButton.

  6. A sixth argument can also be used if needed:

MessageBoxOptions.

There are no limits for arguments 1 and 2 — any character string is OK. As the string becomes longer the message box becomes wider and longer.

The third argument, MessageBoxButtons, has these choices:

  • AbortRetryIgnore (three buttons)

  • OK (one button)

  • OKCancel (two buttons)

  • RetryCancel (two buttons)

  • YesNo (two buttons)

  • YesNoCancel (three buttons)

The fourth argument, MessageBoxIcon, has these choices:

  • Asterisk (a lowercase letter “i” in a circle)

  • Error (a white “X” in a circle with a red background)

  • Exclamation (an exclamation point in a triangle with a yellow background)

  • Hand (same as Error)

  • Information (same as Asterisk)

  • None (no symbols)

  • Question (a question mark in a circle)

  • Stop (same as Error)

  • Warning (same as Exclamation)

The fifth argument, MessageBoxDefaultButton, has these choices:

  • Button1 (first button on the message box is highlighted as the default)

  • Button2 (second button on the message box is highlighted as the default)

  • Button3 (third button on the message box is highlighted as the default)

The sixth argument, MessageBoxOptions, has these choices:

  • DefaultDesktopOnly (message box is displayed on the active desktop)

  • RightAlign (message box text is right-aligned)

  • RtlReading (message box text is displayed backward (right to left)

  • ServiceNotification (same as DefaultDesktopOnly)

The DialogResult dr argument includes:

  • DialogResult.Yes

  • DialogResult.No

  • DialogResult.Cancel

  • DialogResult.OK

  • DialogResult.Abort

  • DialogResult.Retry

  • DialogResult.Ignore

This automated message box scheme should meet 99 percent of your needs as a programmer. But if you look closely at the message box provided by Visual Studio C#, you soon realize that every reply to be received from the user must fit into the vocabulary Yes, No, Cancel, OK, Abort, Retry, or Ignore.

What if the question that has to be asked of the user is “In what decibel range do you want the speakers to broadcast?” In this case you are forced to build your own message box. This next section shows you how to do so.


A General Dialog Box

Suppose the standard Visual Studio C# message box does not meet your needs. (For example, you need options like “50deciBel,” “100deciBel,” and “200deciBel.”) You could use a three-button message box provided by Visual Studio C#, like the YesNoCancel choice for the third argument above. You could advise the user to pick the Yes button if “50deciBel” was desired, the No button if “100deciBel” is desired, or Cancel if the “200deciBel” choice is desired. But that is an archaic approach to this problem.

A more appropriate solution may be to create your own message box. The remainder of this chapter shows you how to create a general dialog box from scratch.

Image from book
Figure 13-1: General Dialog entry (example) — Form1
Image from book
Figure 13-2: General Dialog — Form2

Form1 is the form the programmer is working on when he must ask the user a question about which volume setting is desired. Form2 must be created at this time to pose the question to the user about the decibel setting. To call the general dialog, lines GD022-GD023 are invoked (see the listing below). Lines GD025-GD036 receive the response from the user. For a discussion on how this data is passed between Form2 and Form1, see Chapter 5.

Form2 is the message box that asks the user to pick a decibel level. The user picks button1, button2, or button3. Each button has a separate event handler (lines GD130-GD156). The main window receives the decibel answer from line GD132, GD142, or GD152.

This general dialog works under all circumstances. It is more difficult to create than the standard MessageBox.Show, but it allows the programmer to ask the right question(s) of the user. Lines GD022, 113, 115, and 117 make the transfer of data from the new message box back to the main program possible.

A complete copy of project GeneralDialog is located at Visual Studio 2005\Projects\DemosSourceCode. To execute this project, bring it into the IDE, compile it, and run it.

GeneralDialog (GD) Listing

Form1.cs:
GD001:        #region Using directives
GD002:        using System;
GD003:        using System.Collections.Generic;
GD004:        using System.ComponentModel;
GD005:        using System.Data;
GD006:        using System.Drawing;
GD007:        using System.Windows.Forms;
GD008:        #endregion

GD009:        namespace GeneralDialog
GD010:        {
GD011:          partial class Form1 : Form
GD012:          {
GD013:            public int intAnswerFromGeneralDialog;

GD014:            public Form1()
GD015:            {InitializeComponent();}
//-----------------------------------------------------------------------------------------//
GD020:            private void button1_Click(object sender, System.EventArgs e)
GD021:            { // Get the general dialog. Include the main window handle, 'this',
                    // to send information back.
GD022:              Form2 frm2 = new Form2(this);
GD023:              frm2.ShowDialog();
GD024:              frm2.Close();

                    // Move on to the remainder of the program. This is the response
                    // from the General Dialog:
GD025:              if(intAnswerFromGeneralDialog == 50) // 50 dB.
GD026:              { // Do this.
GD027:                MessageBox.Show("You picked '50 db'.");
GD028:              }
GD029:              else if(intAnswerFromGeneralDialog == 100) // 100 dB.
GD030:              { // Do this.
GD031:                MessageBox.Show("You picked '100 dB'.");
GD032:              }
GD033:              else // 200 dB.
GD034:              { // Do this.
GD035:                MessageBox.Show("You picked '200 dB'.");
GD036:              }
GD037:            }
//-----------------------------------------------------------------------------------------//
GD040:            private void button2_Click(object sender, EventArgs e)
GD041:            { // Quit.
GD042:              Close();
GD043:            }
GD044:          }
GD045:        }
//=========================================================================================//
Form1.Designer.cs:
GD050:        namespace GeneralDialog
GD051:        {
GD052:          partial class Form1
GD053:          {
                  // Required designer variable.
GD054:            private System.ComponentModel.IContainer components = null;
                  // Clean up any resources being used.
GD055:            protected override void Dispose(bool disposing)
GD056:            {
GD057:              if (disposing && (components != null)) components.Dispose();
GD058:              base.Dispose(disposing);
GD059:            }

GD060:            #region Windows Form Designer generated code
                  // Required method for Designer support.
GD061:            private void InitializeComponent()
GD062:            {
GD063:              this.button1 = new System.Windows.Forms.Button();
GD064:              this.button2 = new System.Windows.Forms.Button();
GD065:              this.SuspendLayout();
                    //
                    // button1
                    //
GD066:              this.button1.Location = new System.Drawing.Point(125, 42);
GD067:              this.button1.Name = "button1";
GD068:              this.button1.Size = new System.Drawing.Size(150, 23);
GD069:              this.button1.TabIndex = 0;
GD070:              this.button1.Text = "Get the General Dialog";
GD071:              this.button1.Click += new System.EventHandler(this.button1_Click);
                    //
                    // button2
                    //
GD072:              this.button2.Location = new System.Drawing.Point(162, 92);
GD073:              this.button2.Name = "button2";
GD074:              this.button2.TabIndex = 1;
GD075:              this.button2.Text = "Quit";
GD076:              this.button2.Click += new System.EventHandler(this.button2_Click);
                    //
                    // Form1
                    //
GD077:              this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
GD078:              this.ClientSize = new System.Drawing.Size(384, 132);
GD079:              this.Controls.Add(this.button2);
GD080:              this.Controls.Add(this.button1);
GD081:              this.Name = "Form1";
GD082:              this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
GD083:              this.Text = "General Dialog";
GD084:              this.ResumeLayout(false);
GD085:            }
GD086:            #endregion

GD087:            private System.Windows.Forms.Button button1;
GD088:            private System.Windows.Forms.Button button2;
GD089:          }
GD090:        }
//=========================================================================================//
Form2.cs:
GD100:        #region Using directives
GD101:        using System;
GD102:        using System.Collections.Generic;
GD103:        using System.ComponentModel;
GD104:        using System.Data;
GD105:        using System.Drawing;
GD106:        using System.Text;
GD107:        using System.Windows.Forms;
GD108:        #endregion

GD109:        namespace GeneralDialog
GD110:        {
GD111:          partial class Form2 : Form
GD112:          {
                  // Must add this line to communicate with the main window.
GD113:            public Form1 frm1Parent;

GD114:            // Must amend this next line to get the main window handle.
GD115:            public Form2(Form1 frm1)
GD116:            {
                    // Put the main window handle into frm1Parent:
GD117:              frm1Parent = frm1;
                    // Leave this command here; the system needs it:
GD118:              InitializeComponent();
GD119:            }
//-----------------------------------------------------------------------------------------//
GD130:            private void button1_Click(object sender, EventArgs e)
GD131:            { // 50 dB.
GD132:              frm1Parent.intAnswerFromGeneralDialog = 50;
GD133:              Close();
GD134:            }
//-----------------------------------------------------------------------------------------//
GD140:            private void button2_Click(object sender, EventArgs e)
GD141:            { // 100 dB.
GD142:              frm1Parent.intAnswerFromGeneralDialog = 100;
GD143:              Close();
GD144:            }
//-----------------------------------------------------------------------------------------//
GD150:            private void button3_Click(object sender, EventArgs e)
GD151:            { // 200 dB.
GD152:              frm1Parent.intAnswerFromGeneralDialog = 200;
GD153:              Close();
GD154:            }
GD155:          }
GD156:        }
//=========================================================================================//
Form2.Designer.cs:
GD160:        namespace GeneralDialog
GD161:        {
GD162:          partial class Form2
GD163:          {
                  // Required designer variable.
GD164:            private System.ComponentModel.IContainer components = null;
                  // Clean up any resources being used.
GD165:            protected override void Dispose(bool disposing)
GD166:            {
GD167:              if (disposing && (components != null)) components.Dispose();
GD168:              base.Dispose(disposing);
GD169:            }

GD170:            #region Windows Form Designer generated code
                  // Required method for Designer support.
GD171:            private void InitializeComponent()
GD172:            {
GD173:              this.button1 = new System.Windows.Forms.Button();
GD174:              this.label1 = new System.Windows.Forms.Label();
GD175:              this.button2 = new System.Windows.Forms.Button();
GD176:              this.button3 = new System.Windows.Forms.Button();
GD177:              this.SuspendLayout();
                    //
                    // button1
                    //
GD178:              this.button1.Location = new System.Drawing.Point(44, 48);
GD179:              this.button1.Name = "button1";
GD180:              this.button1.TabIndex = 0;
GD181:              this.button1.Text = "50 dB";
GD182:              this.button1.Click += new System.EventHandler(this.button1_Click);
                    //
                    // label1
                    //
GD183:              this.label1.AutoSize = true;
GD184:              this.label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F,
                      System.Drawing.FontStyle.Underline, System.Drawing.GraphicsUnit.Point,
                      ((byte)(0)));
GD185:              this.label1.Location = new System.Drawing.Point(42, 13);
GD186:              this.label1.Name = "label1";
GD187:              this.label1.Size = new System.Drawing.Size(302, 21);
GD188:              this.label1.TabIndex = 1;
GD189:              this.label1.Text = "Pick a Volume Setting (50, 100, 200 dB):";
                    //
                    // button2
                    //
GD190:              this.button2.Location = new System.Drawing.Point(163, 48);
GD191:              this.button2.Name = "button2";
GD192:              this.button2.TabIndex = 2;
GD193:              this.button2.Text = "100 dB";
GD194:              this.button2.Click += new System.EventHandler(this.button2_Click);
                    //
                    // button3
                    //
GD195:              this.button3.Location = new System.Drawing.Point(282, 48);
GD196:              this.button3.Name = "button3";
GD197:              this.button3.TabIndex = 3;
GD198:              this.button3.Text = "200 dB";
GD199:              this.button3.Click += new System.EventHandler(this.button3_Click);
                    //
                    // Form2
                    //
GD200:              this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
GD201:              this.ClientSize = new System.Drawing.Size(384, 96);
GD202:              this.Controls.Add(this.button3);
GD203:              this.Controls.Add(this.button2);
GD204:              this.Controls.Add(this.label1);
GD205:              this.Controls.Add(this.button1);
GD206:              this.Name = "Form2";
GD207:              this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
GD208:              this.Text = "GD Choices";
GD209:              this.ResumeLayout(false);
GD210:              this.PerformLayout();
GD211:            }
GD212:            #endregion

GD213:            private System.Windows.Forms.Button button1;
GD214:            private System.Windows.Forms.Label label1;
GD215:            private System.Windows.Forms.Button button2;
GD216:            private System.Windows.Forms.Button button3;
GD217:          }
GD218:        }


Chapter 14: Displaying Data (Lists) on a Window

Overview

Because of the uncertainties involved in formatting and outputting large amounts of data to a printer, some programmers choose to display the data on a video screen only. They usually remind the user that the data on the screen can be copied to a local printer (one screen at a time) by using the universal Windows sequence of screen print commands:

  1. Highlight the screen (with the data on it) with the cursor.

  2. Press Alt+PrtScreen.

  3. Open the word processor and place the cursor at the top of the page.

  4. Press Ctrl+V to place the screen onto the word processor page.

  5. Print the page.

  6. Decide whether or not to save the page as a word processor document.


Display Preparation by the Programmer

Preparing data for presentation on the screen is not all that easy for the programmer. First of all, the programmer must set up a dummy screen that will receive the data and present it. This involves a great deal of layout time. Second, if a series of labels are created on a window to display the data, each time the data is presented every label must be filled (some with data and some with blanks). There are no automatic refresh schemes for window displays.


Project DisplayAFile

We will demonstrate some of the file display problems using code and a file from Keeping Track.

  1. Move to the desktop, click on the Visual Studio C# icon, and open the IDE. Click on File, followed by Open, then Project/Solution. When the Open Project window is displayed, move to directory Visual Studio 2005\Projects\DemosSourceCode\DisplayAFile, and double-click on project DisplayAFile to bring it into the IDE.

  2. Compile and run the project. The main window looks like this:

    Image from book
    Figure 14-1: DisplayAFile main window

When the user clicks on the Display File button, this window appears:

Before Figure 14-2 could be executed, a layout for this display was created by a programmer in Form2 of this project. The layout looks like this:

Image from book
Figure 14-2: View File ‘KTBuy.dta’ window
Image from book
Figure 14-3: DisplayAFile layout

As we placed each label on the window template for Form2 we changed the label text from “label22” (for example) to “L22” to show that we had completed the placement of that label.

Form1 of this project opens file KTBuy.dta (lines DF0022-DF0031) and determines the number of records in the file (line DF0032). Lines DF0034-DF0044 determine how many times Form2 must be called to display all the records (NOReps) since only 15 records are displayed on each screen. Lines DF0045-DF0053 create Form2 NOReps times.

Note

Note the comment between lines DF0024 and DF0025. In the Visual Studio C# compiler, if you place a local file to be read by the compiled program into the group of files in the project folder, the program cannot access the file. Instead, the file must be placed in a subdirectory two levels below the main directory — in bin/debug with the executable file.

Partial DisplayAFile (DF) Listing

Form1.cs:
DF0002:       using System;
DF0003:       using System.Collections.Generic;
DF0004:       using System.ComponentModel;
DF0005:       using System.Data;
DF0006:       using System.Drawing;
DF0007:       using System.Windows.Forms;
DF0008:       using System.IO; // FileStream.

DF0010:       namespace DisplayAFile
DF0011:       {
DF0012:         partial class Form1 : Form
DF0013:         {
DF0014:           public int kkForm1, NORecs; // NOReps;
DF0015:           public Form1()
DF0016:           { InitializeComponent();}
//------------------------------------------------------------------------------------------//
DF0020:           private void button1_Click(object sender, EventArgs e)
DF0021:           { // Display file 'KTBuy.dta'.
                    // Determine the number of records in 'KTBuy.dta'.
DF0022:             FileStream fs;
DF0023:             try
DF0024:             {
                      // Important: To read this 'local' file KTBuy.dta, you must place
                      // the file in the 'DisplayAFile' folder, subdirectory 'bin\debug'.

DF0025:               fs = new FileStream("KTBuy.dta", FileMode.Open, FileAccess.Read);
DF0026:             }
DF0027:             catch
DF0028:             {
DF0029:               MessageBox.Show("Cannot open 'KTBuy.dta' to count records.");
DF0030:               return;
DF0031:             }

DF0032:             NORecs = (int) (fs.Length / 32);
DF0033:             fs.Close();

DF0034:             int remainder = NORecs;
DF0035:             int NOReps = 0;
DF0036:             if(remainder <= 15) NOReps = 0;
DF0037:             else
DF0038:             {
DF0039:               while(remainder >= 15)
DF0040:               {
DF0041:                 remainder = remainder - 15; // Fifteen records per page.
DF0042:                 NOReps++;
DF0043:               }
DF0044:             }

DF0045:             for(int kk = 0; kk <= NOReps; kk++)
DF0046:             {
DF0047:               kkForm1 = kk;
DF0048:               // Create the Form2 modal window.
DF0049:               Form2 frm2 = new Form2(kkForm1, NORecs); // Send 2 args to Form 2.
DF0050:               frm2.ShowDialog();
DF0051:               frm2.Close();
DF0052:             }
DF0053:           }
//-----------------------------------------------------------------------------------------//
DF0060:           private void button2_Click(object sender, EventArgs e)
DF0061:           { // Quit.
DF0062:             Close();
DF0063:           }
DF0064:         }
DF0065:       }
//==============================================================//
Form1.Designer.cs:
DF0070:       namespace DisplayAFile
DF0071:       {
DF0072:         partial class Form1
DF0073:         {
                  // Required designer variable.
DF0074:           private System.ComponentModel.IContainer components = null;
                  // Clean up any resources being used.
DF0075:           protected override void Dispose(bool disposing)
DF0076:           {
DF0077:             if (disposing && (components != null)) components.Dispose();
DF0078:             base.Dispose(disposing);
DF0079:           }

DF0080:           #region Windows Form Designer generated code
                  // Required method for Designer support.
DF0081:           private void InitializeComponent()
DF0082:           {
DF0083:             this.label1 = new System.Windows.Forms.Label();
DF0084:             this.label2 = new System.Windows.Forms.Label();
DF0085:             this.label3 = new System.Windows.Forms.Label();
DF0086:             this.button1 = new System.Windows.Forms.Button();
DF0087:             this.button2 = new System.Windows.Forms.Button();
DF0088:             this.SuspendLayout();
                    //
                    // label1
                    //
DF0089:             this.label1.AutoSize = true;
DF0090:             this.label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 14F,
                      System.Drawing.FontStyle.Underline, System.Drawing.GraphicsUnit.Point,
                      ((byte)(0)));
DF0091:             this.label1.Location = new System.Drawing.Point(145, 11);
DF0092:             this.label1.Name = "label1";
DF0093:             this.label1.Size = new System.Drawing.Size(209, 24);
DF0094:             this.label1.TabIndex = 0;
DF0095:             this.label1.Text = "Display File \'KTBuy.dta\'";
                    //
                    // label2
                    //
DF0096:             this.label2.AutoSize = true;
DF0097:             this.label2.Location = new System.Drawing.Point(24, 50);
DF0098:             this.label2.Name = "label2";
DF0099:             this.label2.Size = new System.Drawing.Size(433, 17);
DF0100:             this.label2.TabIndex = 1;
DF0101:             this.label2.Text = "This display consists of five columns of data. File
                      \'KTBuy.dta\' contains";
                    //
                    // label3
                    //
DF0102:             this.label3.AutoSize = true;
DF0103:             this.label3.Location = new System.Drawing.Point(24, 65);
DF0104:             this.label3.Name = "label3";
DF0105:             this.label3.Size = new System.Drawing.Size(73, 17);
DF0106:             this.label3.TabIndex = 2;
DF0107:             this.label3.Text = "26 records.";
                    //
                    // button1
                    //
DF0108:             this.button1.Font = new System.Drawing.Font("Microsoft Sans Serif", 10F,
                      System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point,
                      ((byte)(0)));
DF0109:             this.button1.Location = new System.Drawing.Point(150, 89);
DF0110:             this.button1.Name = "button1";
DF0111:             this.button1.Size = new System.Drawing.Size(200, 30);
DF0112:             this.button1.TabIndex = 3;
DF0113:             this.button1.Text = "Display File";
DF0114:             this.button1.Click += new System.EventHandler(this.button1_Click);
                    //
                    // button2
                    //
DF0115:             this.button2.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F,
                      System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point,
                      ((byte)(0)));
DF0116:             this.button2.Location = new System.Drawing.Point(212, 134);
DF0117:             this.button2.Name = "button2";
DF0118:             this.button2.TabIndex = 4;
DF0119:             this.button2.Text = "Quit";
DF0120:             this.button2.Click += new System.EventHandler(this.button2_Click);
                    //
                    // Form1
                    //
DF0121:             this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 16F);
DF0122:             this.ClientSize = new System.Drawing.Size(484, 182);
DF0123:             this.Controls.Add(this.button2);
DF0124:             this.Controls.Add(this.button1);
DF0125:             this.Controls.Add(this.label3);
DF0126:             this.Controls.Add(this.label2);
DF0127:             this.Controls.Add(this.label1);
DF0128:             this.Font = new System.Drawing.Font("Microsoft Sans Serif", 10F,
                      System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point,
                      ((byte)(0)));
DF0129:             this.Name = "Form1";
DF0130:             this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
DF0131:             this.Text = "Display A File";
DF0132:             this.ResumeLayout(false);
DF0133:             this.PerformLayout();
DF0134:           }
DF0135:           #endregion

DF0136:           private System.Windows.Forms.Label label1;
DF0137:           private System.Windows.Forms.Label label2;
DF0138:           private System.Windows.Forms.Label label3;
DF0139:           private System.Windows.Forms.Button button1;
DF0140:           private System.Windows.Forms.Button button2;
DF0141:         }
DF0142:       }

Form2 Sequence

Once Form2 has been created, the event handler Form2_Load is executed. Lines DF0194-DF0198 confirm that the file KTBuy.dta is present, and line DF0199 opens the file to read. This read always starts at the first record, so DF0201-DF0205 read and discard enough records to get to the records of interest. The serious reading of records begins at DF0205.

Notice that the record that is read from the file (DF0205) is read as a char, not as a string. ReadBlock only reads to a char buffer (32 chars). DF0204 takes the item number and prepares it for display in column one of Form2 (label12). Then lines DF0207-DF0211 parse the 32-char record into four variables (stock symbol, year-week of purchase, number of shares, and total price).

Lines DF0216-DF0221 place the data in the second row of the display (labels 17 through 21). But a new check must be made each time before the row is loaded: DF0213 checks to see if ItemNo is less than or equal to the number of records in the file (NOR). Once ItemNo is greater than NOR, the code fills the row of data with blanks (DF0225-DF0229). Every one of the labels (12 through 86) must be filled with something on each pass.

The code moves from the row 1 entries to the row 15 entries, and is finished. When the user is ready to see the next group of records, she clicks on the View the Next Fifteen Buys button. This button and the Quit button both return to Form1, but the Quit button exits the DF0045-DF0052 loop.

Partial Form2 Listing

Form2.cs:
DF0160:       #region Using directives
DF0161:       using System;
DF0162:       using System.Collections.Generic;
DF0163:       using System.ComponentModel;
DF0164:       using System.Data;
DF0165:       using System.Drawing;
DF0166:       using System.Text;
DF0167:       using System.Windows.Forms;
DF0168:       using System.IO; // File.Exists.
DF0169:       #endregion

DF0170:       namespace DisplayAFile
DF0171:       {
DF0172:         partial class Form2 : Form
DF0173:         {
DF0174:           public int RepNo, NOR; // Data sent from Form1.

DF0175:           public Form2(int TNumber1, int TNumber2)
DF0176:           {
DF0177:             RepNo = TNumber1; // Data from Form1.
DF0178:             NOR = TNumber2; // Data from Form1.
                    // Required for Windows Form Designer support.
DF0179:             InitializeComponent();
DF0180:           }
//-----------------------------------------------------------------------------------------//
DF0190:           private void Form2_Load(object sender, EventArgs e)
DF0191:           { // Constructor.
DF0192:             string empty = " ";
DF0193:             label2.Text = NOR.ToString();
                    // Open file 'KTBUY.DTA' to read the data.
DF0194:             if (!File.Exists("KTBuy.dta"))
DF0195:             {
DF0196:               MessageBox.Show("Cannot find file 'KTBuy.dta' to read.");
DF0197:               Close();
DF0198:             }

                    // IMPORTANT: To read this 'local' file KTBuy.dta, you must place
                    // the file in the 'DisplayAFile' folder, subdirectory 'bin\debug'.

DF0199:             StreamReader w = new StreamReader("KTBuy.dta");
DF0200:             char[] charRealBlock = new char[33];

                    // Read and discard as many records as necessary to place the file
                    // pointer at the start of the proper record to read.
DF0201:             if (RepNo != 0)
DF0202:               for (int mm = 0; mm < 15 * RepNo; mm++)
DF0203:               w.ReadBlock(charRealBlock, 0, 32);

                    // Begin reading the file.
DF0204:             int ItemNo=1+15*RepNo; // #1
DF0205:             w.ReadBlock(charRealBlock, 0, 32);

DF0206:             label12.Text = ItemNo.ToString();
DF0207:             label13.Text = new string(charRealBlock, 0, 5);
DF0208:             label14.Text = new string(charRealBlock, 6, 5);
DF0209:             label15.Text = new string(charRealBlock, 12, 9);
DF0210:             label16.Text = new string(charRealBlock, 22, 9);
DF0211:             label9.Text = (NOR - ItemNo).ToString();

DF0212:             ItemNo++; // #2
DF0213:             if (ItemNo <= NOR)
DF0214:             {
DF0215:               w.ReadBlock(charRealBlock, 0, 32);
DF0216:               label17.Text = ItemNo.ToString();
DF0217:               label18.Text = new string(charRealBlock, 0, 5);
DF0218:               label19.Text = new string(charRealBlock, 6, 5);
DF0219:               label20.Text = new string(charRealBlock, 12, 9);
DF0220:               label21.Text = new string(charRealBlock, 22, 9);
DF0221:               label9.Text = (NOR - ItemNo).ToString();
DF0222:             }
DF0223:             else
DF0224:             {
DF0225:               label17.Text = empty;
DF0226:               label18.Text = empty;
DF0227:               label19.Text = empty;
DF0228:               label20.Text = empty;
DF0229:               label21.Text = empty;

DF0230:             }

[Repeat for items #3 through #15]

DF0478:             w.Close(); // Close the file being read.
DF0479:           } // End of Form2_Load().
//-----------------------------------------------------------------------------------------//
DF0490:           private void button1_Click(object sender, EventArgs e)
DF0491:           { // View the next fifteen buys.
DF0492:             Close();
DF0493:           }
//-----------------------------------------------------------------------------------------//
DF0500:           private void button2_Click(object sender, EventArgs e)
DF0501:           { // Quit.
DF0502:             Close();
DF0503:           }
DF0504:         }
DF0505:       }
//=========================================================================================//
Form2.Designer.cs:
DF0510:       namespace DisplayAFile
DF0511:       {
DF0512:         partial class Form2
DF0513:         {
                  // Required designer variable.
DF0514:           private System.ComponentModel.IContainer components = null;
                  // Clean up any resources being used.
DF0515:           protected override void Dispose(bool disposing)
DF0516:           {
DF0517:             if (disposing && (components != null)) components.Dispose();
DF0518:             base.Dispose(disposing);
DF0519:           }

DF0520:           #region Windows Form Designer generated code
                  // Required method for Designer support.
DF0521:           private void InitializeComponent()
DF0522:           {
DF0523:             this.button1 = new System.Windows.Forms.Button();
DF0524:             this.button2 = new System.Windows.Forms.Button();
DF0525:             this.label1 = new System.Windows.Forms.Label();

[Labels 2 through 86]

DF0611:             this.SuspendLayout();
                    //
                    // button1
                    //
DF0612:             this.button1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F,
                      System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point,
                      ((byte)(0)));
DF0613:             this.button1.Location = new System.Drawing.Point(176, 370);
DF0614:             this.button1.Name = "button1";
DF0615:             this.button1.Size = new System.Drawing.Size(161, 25);
DF0616:             this.button1.TabIndex = 0;
DF0617:             this.button1.Text = "View the Next Fifteen Buys";
DF0618:             this.button1.Click += new System.EventHandler(this.button1_Click);
                    //
                    // button2
                    //
DF0619:             this.button2.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F,
                      System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point,
                      ((byte)(0)));
DF0620:             this.button2.Location = new System.Drawing.Point(400, 370);
DF0621:             this.button2.Name = "button2";
DF0622:             this.button2.Size = new System.Drawing.Size(75, 25);
DF0623:             this.button2.TabIndex = 1;
DF0624:             this.button2.Text = "Quit";
DF0625:             this.button2.Click += new System.EventHandler(this.button2_Click);
                    //
                    // label1
                    //
DF0626:             this.label1.AutoSize = true;
DF0627:             this.label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F,
                      System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point,
                      ((byte)(0)));
DF0628:             this.label1.Location = new System.Drawing.Point(16, 13);
DF0629:             this.label1.Name = "label1";
DF0630:             this.label1.Size = new System.Drawing.Size(117, 14);
DF0631:             this.label1.TabIndex = 2;
DF0632:             this.label1.Text = "# of records in this file:";

[Labels 2 through 86]

                    //
                    // Form2
                    //
DF1313:             this.AutoScaleBaseSize = new System.Drawing.Size(7, 16);
DF1314:             this.ClientSize = new System.Drawing.Size(529, 404);
DF1315:             this.Controls.Add(this.label86);

[Controls for Labels 85 through 1]

DF1401:             this.Font = new System.Drawing.Font("Microsoft Sans Serif", 10F,
                      System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point,
                      ((byte)(0)));
DF1402:             this.Name = "Form2";
DF1403:             this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
DF1404:             this.Text = "View File \'KTBuy.dta\'";
DF1405:             this.Load += new System.EventHandler(this.Form2_Load);
DF1406:             this.ResumeLayout(false);
DF1407:             this.PerformLayout();
DF1408:           }
DF1409:           #endregion

DF1410:           private System.Windows.Forms.Button button1;
DF1411:           private System.Windows.Forms.Button button2;
DF1412:           private System.Windows.Forms.Label label1;

[Labels 2 through 86]

DF1499:           private System.Windows.Forms.Label label86;
DF1500:         }
DF1501:       }

A complete listing of this project may be found at Visual Studio 2005\Tagged KT-Demo Code\App D -- DemoVC#TaggedList.doc (DisplayAFile).


Chapter 15: Plotting Data on a Window

Overview

Most programmers enjoy creating plotting routines because they deliver so much “bang for the buck.” It’s one thing to calculate analysis data for a user, and another to plot the data so the user can see the data trends visually. The visual display wins every time.

In this section we take a file from the Keeping Track series, WeeklyNetWorth.dta, and plot it on the screen. The X axis (horizontal) is Weeks of the Year, and the Y axis (vertical) is $$. This plot has some limitations:

  • Only the last 52 weeks of data is plotted. The file WeeklyNetWorth.dta contains data from the 44th week in 2003 to the 11th week in 2005, but it will only plot data from the 11th week of 2004 to the 11th week of 2005 (the latest 52 weeks in the file).

  • The code shown here is a subset of the plotters in the Keeping Track suite; this code plots the net worth of a financial portfolio only; the complete code in Keeping Track plots both the weekly net worth and the weekly net gain (present value – original cost) as two separate plots.

Most PC graphics cards used in conjunction with Microsoft operating systems allow four pixel settings on the screen:

  • 800 x 600 (coarsest setting)

  • 1024 x 768

  • 1280 x 1024

  • 1600 x 1200 (finest setting)

The programmer must be sure that the plot she creates will fit on the screen, so one assumes that the coarsest setting will be used on the user’s PC. We have already mentioned that the Visual Studio C# compiler makes provision in its software to place each window in the center of the screen, but it does not provide for a variable number of pixels in length and height of a window. So as the user moves from the most coarse setting to the finest setting, the window becomes smaller and smaller on the screen.

Some portion of the screen area must be used to list the X and Y coordinates of the plot (below and to the left of the plot). At the coarsest 800 x 600 setting, the maximum plot size is about 600 pixels wide x 450 pixels high; these are the numbers used in this plot.


Project DataPlotter

  1. Move to the desktop, and click on the Visual Studio C# icon to bring-up the IDE. Click on File followed by Open, then Project/Solution. When the Open Project window is displayed, move to directory Visual Studio 2005\Projects\DemosSourceCode\ DataPlotter, and double-click on project DataPlotter to bring it into the IDE.

  2. Compile and run the project. The main window looks like this:

    Image from book
    Figure 15-1: Data Plotter main window

This is what the plot looks like:

Image from book
Figure 15-2: Plot of net worth

The vertical green line on the right edge of the plot is the end of 2004 and the start of 2005.

Form1 of this project opens file KTWeeklyNetWorth in lines DP022-DP031. Line DP032 determines the number of records in the file (NONWs). If there are fewer than four points to plot, the plot is aborted in lines DP039-DP043. Form2 is created in lines DP044-DP045.

DataPlotter (DP) Listing

Form1.cs:
DP002:        using System;
DP003:        using System.Collections.Generic;
DP004:        using System.ComponentModel;
DP005:        using System.Data;
DP006:        using System.Drawing;
DP007:        using System.Windows.Forms;
DP008:        using System.IO; // 'FileStream'.

DP010:        namespace DataPlotter
DP011:        {
DP012:          partial class Form1 : Form
DP013:          {
DP014:            public int NONWs;

DP015:            public Form1()
DP016:            {InitializeComponent(); }
//-----------------------------------------------------------------------------------------//
DP020:            private void button1_Click(object sender, EventArgs e)
DP021:            { // Plot Net Worth.
                    // Open file 'KTWeeklyNetWorth.dta' to count records.
DP022:              FileStream fj;
DP023:              try
DP024:              {
                      // IMPORTANT: To read this 'local' file KTWeeklyNetWorth.dta, you must
                      // place the file in the 'DataPlotter' folder, subdirectory 'bin\debug'.
                      // That is because 'bin\debug' is where the executable file is located.
DP025:                fj = new FileStream("KTWeeklyNetWorth.dta", FileMode.Open,
                               FileAccess.Read);
DP026:              }
DP027:              catch
DP028:              {
DP029:                MessageBox.Show("Cannot open 'KTWeeklyNetWorth.dta' to count records.");
DP030:                return;
DP031:              }

DP032:              NONWs = (int) (fj.Length / 47);
DP033:              fj.Close();
DP034:              if(NONWs == 0)
DP035:              {
DP036:                MessageBox.Show("KTWeeklyNetWorth.dta is an Empty File.");
DP037:                return;
DP038:              }

DP039:              if(NONWs >> 4)
DP040:              {
DP041:                MessageBox.Show("There are too few points to plot [less than 4].");
DP042:                return;
DP043:              }

                    // Create the Form2 modal window.
DP044:              Form2 frm2 = new Form2(this); // 'this' required to send info to Form2.
DP045:              frm2.ShowDialog();
DP046:              frm2.Close();
DP047:            }
//-----------------------------------------------------------------------------------------//
DP050:            private void button2_Click(object sender, EventArgs e)
DP051:            { // Quit.
DP052:              Close();
DP053:            }
DP054:          }
DP055:        }
//=========================================================================================//
Form1.Designer.cs:
DP060:        namespace DataPlotter
DP061:        {
DP062:          partial class Form1
DP063:          {
                  // Required designer variable.
DP064:            private System.ComponentModel.IContainer components = null;
                  // Clean up any resources being used.
DP065:            protected override void Dispose(bool disposing)
DP066:            {
DP067:              if (disposing && (components != null)) components.Dispose();
DP068:              base.Dispose(disposing);
DP069:            }

DP070:            #region Windows Form Designer generated code
                  // Required method for Designer support.
DP071:            private void InitializeComponent()
DP072:            {
DP073:              this.label1 = new System.Windows.Forms.Label();
DP074:              this.label2 = new System.Windows.Forms.Label();
DP075:              this.button1 = new System.Windows.Forms.Button();
DP076:              this.button2 = new System.Windows.Forms.Button();
DP077:              this.SuspendLayout();
                    //
                    // label1
                    //
DP078:              this.label1.AutoSize = true;
DP079:              this.label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 14F,
                      System.Drawing.FontStyle.Underline, System.Drawing.GraphicsUnit.Point,
                      ((byte)(0)));
DP080:              this.label1.Location = new System.Drawing.Point(181, 17);
DP081:              this.label1.Name = "label1";
DP082:              this.label1.Size = new System.Drawing.Size(108, 24);
DP083:              this.label1.TabIndex = 0;
DP084:              this.label1.Text = "Data Plotter";
                    //
                    // label2
                    //
DP085:              this.label2.AutoSize = true;
DP086:              this.label2.Font = new System.Drawing.Font("Microsoft Sans Serif", 10F,
                      System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point,
                      ((byte)(0)));
DP087:              this.label2.Location = new System.Drawing.Point(10, 64);
DP088:              this.label2.Name = "label2";
DP089:              this.label2.Size = new System.Drawing.Size(472, 17);
DP090:              this.label2.TabIndex = 1;
DP091:              this.label2.Text = "This program plots a set of \'Net Worth\' values from
                      file \'WeeklyNetWorth.dta\'.";
                    //
                    // button1
                    //
DP092:              this.button1.Location = new System.Drawing.Point(187, 101);
DP093:              this.button1.Name = "button1";
DP094:              this.button1.Size = new System.Drawing.Size(125, 25);
DP095:              this.button1.TabIndex = 2;
DP096:              this.button1.Text = "Plot Net Worth";
DP097:              this.button1.Click += new System.EventHandler(this.button1_Click);
                    //
                    // button2
                    //
DP098:              this.button2.Location = new System.Drawing.Point(210, 141);
DP099:              this.button2.Name = "button2";
DP100:              this.button2.Size = new System.Drawing.Size(75, 25);
DP101:              this.button2.TabIndex = 3;
DP102:              this.button2.Text = "Quit";
DP103:              this.button2.Click += new System.EventHandler(this.button2_Click);
                    //
                    // Form1
                    //
DP104:              this.AutoScaleDimensions = new System.Drawing.SizeF(5F, 13F);
DP105:              this.ClientSize = new System.Drawing.Size(484, 182);
DP106:              this.Controls.Add(this.button2);
DP107:              this.Controls.Add(this.button1);
DP108:              this.Controls.Add(this.label2);
DP109:              this.Controls.Add(this.label1);
DP110:              this.Name = "Form1";
DP111:              this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
DP112:              this.Text = "Data Plot";
DP113:              this.ResumeLayout(false);
DP114:              this.PerformLayout();
DP115:            }
DP116:            #endregion

DP117:            private System.Windows.Forms.Label label1;
DP118:            private System.Windows.Forms.Label label2;
DP119:            private System.Windows.Forms.Button button1;
DP120:            private System.Windows.Forms.Button button2;
DP121:          }
DP122:        }

Form2 Sequence

In Form2, all computations to place the plot on the screen are made in the Form2_Load event handler. Please note the comment between lines DP172 and DP173 about the placement of the KTWeekly NetWorth.dta file so project DataPlotter can access the file.

A loop between lines DP177 and DP186 reads the file one record at a time and the data to be plotted is placed in an array, dTTWorth[mm]. Line DP185 removes any digits past the decimal point. Lines DP188-DP204 alphabetize the list so the data is placed in ascending order with respect to the X axis number, intShortYW. Line DP205 sets the space between each data point along the X axis at 12 pixels. Lines DP207-DP209 determine the X span of the plot (52 weeks or less). The first point to be plotted in dttWorth[mm] is in position mm = ASP (array start point), and the last point to be plotted is in position mm = AEP (array end point).

Lines DP210-DP212 determine whether the plot is split between two years, and the location of that split is set at intYearStartXCoord in line DP238. Lines DP239 and DP240 determine the minimum and maximum Y coordinate values, and line DP246 sets the deltaY between the min and max values. Since there are 400 pixels allocated vertically to plot the data, dTTYInc is calculated in line DP247.

If the data resolution is so fine that the plot would result in a narrow horizontal plot (and an almost straight line), line DP248 intervenes and abandons the plot.

Lines DP256-DP260 determine the value of the five markings along the Y axis. There are only two numbers entered along the X axis: intShortYW[ASP] and intShortYW[AEP].

Form2 Listing

Form2.cs:
DP131:        using System;
DP132:        using System.Collections.Generic;
DP133:        using System.ComponentModel;
DP134:        using System.Data;
DP135:        using System.Drawing;
DP136:        using System.Drawing.Drawing2D; // 'DashStyle'.
DP137:        using System.Text;
DP138:        using System.Windows.Forms;
DP139:        using System.IO; // 'StreamReader'.

DP141:        namespace DataPlotter
DP142:        {
DP143:          partial class Form2 : Form
DP144:          {
DP145:            public Form1 frmParent;
DP146:            public int intXOffset = 70;
DP147:            public int intYOffset = 450;
DP148:            public string[] strYText = new string[5];

DP149:            public Form2(Form1 frm)
DP150:            {
DP151:              frmParent = frm;
DP152:              InitializeComponent();
DP153:            }

DP154:            string strWNWBlock, strShortYW, strTTWorth; // strTTCost;
DP155:            int[] intShortYW = new int[60];
DP156:            decimal[] dTTWorth = new decimal[60];
DP157:            int ASP;  // Array start position.
DP158:            int AEP;  // Array end position.
DP159:            int intWeekSpan;
DP160:            int intYearStartXCoord;
DP161:            decimal dTTYBaseline, dTTYInc;
DP162:            int intLocalNONWs, intDeltaX, intPPDelta, intSplitYear;
//------------------------------------------------------------------------------------------//
DP170:            private void Form2_Load(object sender, EventArgs e)
DP171:            { // Plot file 'KTWeeklyNetWorth.dta' Net Worth.
DP172:              char[] charWNWBlock = new char[47];
                    // IMPORTANT: To read this 'local' file KTWeeklyNetWorth.dta, you must
                    // place the file in the 'DataPlotter' folder, subdirectory 'bin\debug'.

DP173:              StreamReader wow = new StreamReader("KTWeeklyNetWorth.dta");
DP174:              intLocalNONWs = frmParent.NONWs;

DP175:              int intYWTop, intYWBot;
DP176:              decimal dTTWorthTop, dTTWorthBot;

DP177:              for (int mm = 0; mm < intLocalNONWs; mm++)
DP178:              {
                      // Read strShortYW, strYW, strTTWorth, strTTCost, and strTTGainLoss.
DP179:                wow.ReadBlock(charWNWBlock, 0, 47);
DP180:                strWNWBlock = new string(charWNWBlock);
DP181:                strShortYW = strWNWBlock.Substring(0, 4);
DP182:                strTTWorth = strWNWBlock.Substring(11, 11);

                      // Convert 'strShortYW' to an integer.
DP183:                intShortYW[mm] = Int32.Parse(strShortYW);
                      // Convert 'strTTWorth' to decimal for plotting.
DP184:                dTTWorth[mm] = Decimal.Parse(strTTWorth);
                      // Set 'TTWorth' for NO decimal places.
DP185:                dTTWorth[mm] = Decimal.Round(dTTWorth[mm], 0);
DP186:              }
DP187:              wow.Close();

                    // Re-order the list so the smallest year-week number moves to the top.
DP188:              for (int kk = 1; kk < intLocalNONWs; kk++)
DP189:              {
DP190:                for (int mm = 1; mm < intLocalNONWs; mm++)
DP191:                {
DP192:                  intYWTop = intShortYW[mm - 1];
DP193:                  intYWBot = intShortYW[mm];
DP194:                  dTTWorthTop = dTTWorth[mm - 1];
DP195:                  dTTWorthBot = dTTWorth[mm];

DP196:                  if (intYWTop > intYWBot)         // Switch
DP197:                  {
DP198:                    intShortYW[mm - 1] = intYWBot;
DP199:                    intShortYW[mm] = intYWTop;
DP200:                    dTTWorth[mm - 1] = dTTWorthBot;
DP201:                    dTTWorth[mm] = dTTWorthTop;
DP202:                  }
DP203:                }
DP204:              }

                    // All data is now in ascending order wrt Year-Week number.
                    // Max of 52 data points, separated by 12 pixels (east-west).
DP205:              intDeltaX = 12;

DP206:              int intNOWIOYear = 0;                 // # of Weeks In Old Year.

                    // Calculate the span of the 52-week plot.
DP207:              int intEndYear = intShortYW[intLocalNONWs - 1] / 100;
DP208:              int intEndWeek = intShortYW[intLocalNONWs - 1] - intEndYear * 100;
DP209:              int intStartYear = intShortYW[0] / 100;

                    // Is the plot split over 2 years?
DP210:              intSplitYear = 1;                           // Yes
DP211:              if (intEndWeek == 52) intSplitYear = 0;     // No
DP212:              else if (intStartYear == intEndYear) intSplitYear = 0;

                    // Calculate the earliest possible 'start Year-Week'.
DP213:              int intEarliestStart = intShortYW[intLocalNONWs - 1] - 100;
DP214:              if (intShortYW[0] >= intEarliestStart)
DP215:              {
DP216:                intEarliestStart = intShortYW[0];
DP217:                ASP = 0;
DP218:              }
DP219:              else            // Is there an 'intShortYW[]' number exactly like
                              'intEarliestStart'?

DP220:              {
DP221:                int mm = 0;
DP222:                while (intShortYW[mm] < intEarliestStart) mm++;
DP223:                intEarliestStart = intShortYW[mm];
DP224:                ASP = mm;
DP225:              }

DP226:              AEP = intLocalNONWs - 1;             // Array end position.

DP227:              int intStartYW = intEarliestStart;
DP228:              int intStartWeek = intStartYW - (intStartYW / 100) * 100;
DP229:              int intEndYW = intShortYW[intLocalNONWs - 1];

                    // Numbers needed by 'Form2_Paint'.
DP230:              intPPDelta = AEP - ASP;

DP231:              if (intSplitYear == 0)               // No split.
DP232:                intWeekSpan = intEndYW - intStartYW;
DP233:              else                                 // Split year.
DP234:              {
DP235:                intWeekSpan = intEndYW - intStartYW - 48;
DP236:                intNOWIOYear = 52 - intStartWeek;  // # of weeks in old year.
DP237:              }
DP238:              intYearStartXCoord = intXOffset + intDeltaX * (intNOWIOYear - 1) + 6;

                    // Prepare 'TTWorth' data.
DP239:              decimal dTTYMin = dTTWorth[0];
DP240:              decimal dTTYMax = dTTWorth[0];
DP241:              for (int kk = 1; kk < intLocalNONWs; kk++)
DP242:              {
DP243:                if (dTTWorth[kk] > dTTYMax) dTTYMax = dTTWorth[kk];
DP244:                if (dTTWorth[kk] < dTTYMin) dTTYMin = dTTWorth[kk];
DP245:              }

DP246:              decimal dTTDeltaY = dTTYMax - dTTYMin;
DP247:              dTTYInc = dTTDeltaY / 400.0m;

DP248:              if ((int)dTTYInc < 1)
DP249:              {
DP250:                MessageBox.Show("The data points are too close together \n"
                      + "vertically to plot with any resolution.");
DP251:                return;
DP252:              }

DP253:              dTTYBaseline = dTTYMin;

                    // Determine the markings to be placed on the 'Y' and 'X' axes.
DP254:              decimal dTTYIncrement = dTTDeltaY / 4.0m;
DP255:              decimal[] TTY = new decimal[5];
DP256:              TTY[0] = dTTYBaseline;
DP257:              TTY[1] = dTTYBaseline + dTTYIncrement;
DP258:              TTY[2] = dTTYBaseline + 2.0m * dTTYIncrement;
DP259:              TTY[3] = dTTYBaseline + 3.0m * dTTYIncrement;
DP260:              TTY[4] = dTTYBaseline + 4.0m * dTTYIncrement;

                    // Round the 'TTY' numbers to NO decimals.
DP261:              for (int jb = 0; jb < 5; jb++) TTY[jb] = Decimal.Round(TTY[jb], 0);
DP262:              for (int jc = 0; jc < 5; jc++) strYText[jc] = TTY[jc].ToString();
DP263:            }
//-----------------------------------------------------------------------------------------//
DP270:          private void button1_Click(object sender, EventArgs e)
DP271:            { // Close the Chart.
DP272:              Close();
DP273:            }

The Form2_Paint event handler takes over the plotting chores. Lines DP282-DP285 name the pen colors and fonts. Lines DP287-DP288 paint the Y axis, and line DP289 draws the X axis. Lines DP290-DP294 paint the numbers along the X axis. Lines DP300-DP301 paint the tick marks along the Y axis, and lines DP302-DP303 paint the tick marks along the X axis. If the plot includes a split year, lines DP304-DP309 paint a vertical green line where one year ends and the next begins.

The first data point is determined in lines DP310-DP315. Lines DP316-DP317 save the location of the data point because it will be needed when the next data point is plotted (and a line will be drawn between the two points). Lines DP318-DP329 create the series of straight lines between the old coordinate and the new coordinate. Line DP333 does all the drawing.

Continuation of Form2 Listing

DP280:            private void Form2_Paint(object sender, PaintEventArgs e)
DP281:            { // Paint the plot.
DP282:              Graphics grfx = e.Graphics;
DP283:              Pen blackPen = new Pen(Color.Black,2);
DP284:              Font font1 = new Font("Arial", 14);
DP285:              Font font2 = new Font("Arial", 10);
DP286:              grfx.DrawLine(blackPen, 70, 50, 70, 450);
                    // Paint the 'Y' axis dollar values.
DP287:              for(int pu = 0; pu < 5; pu++)
DP288:              grfx.DrawString(strYText[pu], font2, Brushes.Black, 20, 445 - 100*pu);
                    // Paint the 'X' axis.
DP289:              grfx.DrawLine(blackPen, 70, 450, 680, 450);
DP290:              string strShYW;
DP291:              strShYW = intShortYW[ASP].ToString();
DP292:              int SLL = strShYW.Length;
DP293:              if(SLL == 3) strShYW = "0" + strShYW;       // Always show 4 characters.
DP294:              grfx.DrawString(strShYW, font2, Brushes.Black, 52, 460);

DP295:              int intEndPoint = 55 + intDeltaX * intWeekSpan;
DP296:              strShYW = intShortYW[intLocalNONWs -1].ToString();
DP297:              SLL = strShYW.Length;
DP298:              if(SLL == 3) strShYW = "0" + strShYW;       // Always show 4 characters.
DP299:              grfx.DrawString(strShYW, font2, Brushes.Black, intEndPoint-3, 460);
                    // Paint tick marks on the 'Y' axis.
DP300:              for(int mm = 0; mm < 5; mm++)
DP301:              grfx.DrawLine(blackPen, 65, 450-100*mm, 75, 450-100*mm);
                    // Paint tick marks on the 'X' axis.
DP302:              for(int mm = 0; mm <= intWeekSpan; mm++)
DP303:              grfx.DrawLine(blackPen, 70+intDeltaX*mm, 445, 70+intDeltaX*mm, 455);
                    // If a split year, paint a vertical line at New Year's week.
DP304:              if(intSplitYear == 1)
DP305:              {
DP306:                Pen greenPen = new Pen(Color.Green, 2);
DP307:                greenPen.DashStyle = (DashStyle) 1;       // Dash.
DP308:                grfx.DrawLine(greenPen, intYearStartXCoord, 450, intYearStartXCoord,
                                    50);
DP309:              }
                    // First point to be plotted.
DP310:              string strAnswer1 = dTTWorth[ASP].ToString();
DP311:              string strAnswer2 = dTTYBaseline.ToString();
DP312:              string strAnswer3 = dTTYInc.ToString();
DP313:              int intAnswer = (int)((dTTWorth[ASP] - dTTYBaseline) / dTTYInc);
DP314:              int intOldYW = intShortYW[ASP];
DP315:              int intGapDelta = 0;

DP316:              int intOldX = intXOffset;
DP317:              int intOldY = intYOffset - intAnswer;

DP318:              for(int mm = 1; mm <= intPPDelta; mm++)
DP319:              {
                      // Check each record to make sure there are no gaps in the data.
DP320:                int intNewYW = intShortYW[ASP+mm];
                      // Determine if the YW has moved into a new year(Ex: from 0252 to 0301).
DP321:                if(intNewYW > (intOldYW + 48))             // Year jump.
DP322:                intGapDelta = intGapDelta + intNewYW - intOldYW - 49;
DP323:                else // No year jump.
DP324:                {
DP325:                  if(intNewYW == intOldYW + 1)            // No gap.
DP326:                  { }
DP327:                  else // Weekly gap exists.
DP328:                  intGapDelta = intGapDelta + intNewYW - intOldYW - 1;;
DP329:                }

DP330:                intOldYW = intNewYW;
DP331:                int intNewX = intXOffset + intDeltaX*mm + intDeltaX*intGapDelta;
DP332:                int intNewY =intYOffset-(int)((dTTWorth[ASP+mm]-dTTYBaseline)/dTTYInc);

DP333:                grfx.DrawLine(blackPen, intOldX, intOldY, intNewX, intNewY);
DP334:                intOldX = intNewX;
DP335:                intOldY = intNewY;
DP333:              }
DP337:            }
DP338:          }
DP339:        }
//========================================================================================//
Form2.Designer.cs:
DP350:        namespace DataPlotter
DP351:        {
DP352:          partial class Form2
DP353:          {
                  // Required designer variable.
DP354:            private System.ComponentModel.IContainer components = null;
                  // Clean up any resources being used.
DP355:            protected override void Dispose(bool disposing)
DP356:            {
DP357:              if (disposing && (components != null)) components.Dispose();
DP358:              base.Dispose(disposing);
DP359:            }

DP360:            #region Windows Form Designer generated code
                  // Required method for Designer support.
DP361:            private void InitializeComponent()
DP362:            {
DP363:              this.label1 = new System.Windows.Forms.Label();
DP364:              this.button1 = new System.Windows.Forms.Button();
DP365:              this.SuspendLayout();
                    //
                    // label1
                    //
DP366:              this.label1.AutoSize = true;
DP367:              this.label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 14F,
                      System.Drawing.FontStyle.Underline, System.Drawing.GraphicsUnit.Point,
                      ((byte)(0)));
DP368:              this.label1.Location = new System.Drawing.Point(287, 8);
DP369:              this.label1.Name = "label1";
DP370:              this.label1.Size = new System.Drawing.Size(123, 24);
DP371:              this.label1.TabIndex = 0;
DP372:              this.label1.Text = "KT Net Worth";
                    //
                    // button1
                    //
DP373:              this.button1.Location = new System.Drawing.Point(564, 8);
DP374:              this.button1.Name = "button1";
DP375:              this.button1.Size = new System.Drawing.Size(100, 25);
DP376:              this.button1.TabIndex = 1;
DP377:              this.button1.Text = "Close the Chart";
DP378:              this.button1.Click += new System.EventHandler(this.button1_Click);
                    //
                    // Form2
                    //
DP379:              this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
DP380:              this.ClientSize = new System.Drawing.Size(704, 452);
DP381:              this.Controls.Add(this.button1);
DP382:              this.Controls.Add(this.label1);
DP383:              this.Name = "Form2";
DP384:              this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
DP385:              this.Text = "Plot file \'KTWeeklyNetWorth.dta\'";
DP386:              this.Paint += new System.Windows.Forms.PaintEventHandler
                      (this.Form2_Paint);
DP387:              this.Load += new System.EventHandler(this.Form2_Load);
DP388:              this.ResumeLayout(false);
DP389:              this.PerformLayout();
DP390:            }
DP391:            #endregion

DP392:            private System.Windows.Forms.Label label1;
DP393:            private System.Windows.Forms.Button button1;
DP394:          }
DP395:        }
Note

For proper presentation on the screen, the Form1_Paint event handler must be actuated at least 30 times per second (since the human eye detects “flicker” at rewriting rates less than 30). Thirty times per second means that Form1_Paint must rewrite in a time less than 1000/30, or 33.3 milliseconds. In the early days of Windows, PCs were clocking at speeds around 20,000 cycles per second, so it was easy to create code in Form1_Paint that could not possibly be completed in 33 milliseconds. At that time it was almost impossible to create a plot with two or more datasets superimposed on the screen because the Form1_Paint handler couldn’t work fast enough to support this many datasets and hold down the flicker factor.


Chapter 16: Printing Text

One of the major tasks that computer programs perform is that of printing hard copy for users. The print commands are straightforward, but there are so many options that one can easily miss the forest for all the trees. In this chapter we perform a small printing task — printing columns of data taken from a file, KTWeeklyNetWorth.dta. To print in Visual Studio C# you must include the “using System.Drawing.Printing” statement at the top of the *.cs source code file.

Project PrintAFile

To display this project, open the IDE using the desktop icon, then click on File on the main menu, followed by Open and Project/Solution. When the Open Project window appears, move to the Visual Studio 2005\Projects\DemosSourceCode\PrintAFile directory and click on project PrintAFile in the list of demo projects. Compile and run the project.

A combo box is included on the main window so the user can choose any one of three printer fonts: Arial, Courier New, or Times New Roman. If you prefer other fonts you can change the project code easily. There is only one window in this project, as shown below:

Image from book
Figure 16-1: Print a File window

When the program first opens, the Print File ‘KTWeeklyNetWorth.dta’ in this Font button is disabled (line PF032 in the listing below). Line PF033 lists the three possible fonts, and lines PF034-PF035 load the font names into comboBox1.

The user must click on the down arrow in the combo box to show the font names, and once she has clicked on one of them (which moves that font name to the top of the box), the Print File… button is enabled (line PF142).

Once the Print File… button has been clicked, the printing process begins:

  1. File KTWeeklyNetWorth.dta is opened in lines PF042-PF051 and the number of records in the file is determined in PF052 (NONWs).

  2. Computations are made in lines PF059-PF066 to determine the number of pages to be printed.

  3. Lines PF067-PF077 begin the printing process.

  4. In the OnPrintPage event handler (which is called in line PF074), computations are made in lines PF100-PF102 to scale the print on the page. By choosing GraphicsUnit.Document in line PF101, the vertical scale is set at 1/300th of an inch. When PageScale is set to 0.5f in line PF102, the vertical scale is now 1/600th of an inch (or 600 dots per inch, vertically).

  5. Lines PF103-PF104 create two fonts, based on the font name that the user picked in comboBox1. font1 is a 14-point font and font2 is a 10-point font (72 points to an inch).

  6. Line PF105 moves all the print 580 pixels to the right (measured from the left edge of the paper).

  7. Line PF107 opens the KTWeeklyNetWorth.dta file one more time (to read the data). Line PF109 prints the first line on the page in the larger font, font1, and lines PF110-PF111 print the remainder of the column labels in font2.

  8. Lines PF115-PF128 are the print loop. Line PF119 reads the file for new data, and lines PF121-PF124 parse the data into four variables (Year-Week, TTWorth, TTCost, and TTGainLoss). Lines PF125-PF126 put the data on the sheet of paper.

PrintAFile (PF) Listing

Form1.cs:
PF002:        using System;
PF003:        using System.Collections.Generic;
PF004:        using System.ComponentModel;
PF005:        using System.Data;
PF006:        using System.Drawing;
PF007:        using System.Drawing.Printing; // Required to print.
PF008:        using System.Windows.Forms;
PF009:        using System.IO; // 'FileStream'.

PF011:        namespace PrintAFile
PF012:        {
PF013:          partial class Form1 : Form
PF014:          {
PF015:            public int NONWs, BigLoop, StartPoint, EndPoint, PageNo;
PF016:            public int[] Loop = new int[5];
PF017:            public char[] charDPI = new char[3];

PF018:            public Form1()
PF019:            {InitializeComponent(); }
//-----------------------------------------------------------------------------------------//
PF030:            private void Form1_Load(object sender, EventArgs e)
PF031:            {
PF032:              button1.Enabled = false;
                    // Place the font names and sizes into the comboBox.
PF033:              string[] strFontNames = new string[] {"Arial", "Courier New",
                      "Times New Roman"};
PF034:              for (int ii = 0; ii < 3; ii++)
PF035:                comboBox1.Items.Add(strFontNames[ii]);
PF036:            }
//-----------------------------------------------------------------------------------------//
PF040:            private void button1_Click(object sender, EventArgs e)
PF041:            { // Print file 'KTWNW.dta' in this font.
                    // Open file 'KTWeeklyNetWorth.dta' to count records.
                    // IMPORTANT: To read this 'local' file 'KTWeeklyNetWorth.dta', you must
                    // place the file in the 'PrintAFile' folder, subdirectory 'bin\debug'.
PF042:              FileStream fk;
PF043:              try
PF044:              {
PF045:                 fk = new FileStream("KTWeeklyNetWorth.dta", FileMode.Open,
                                FileAccess.Read);
PF046:              }
PF047:              catch
PF048:              {
PF049:                MessageBox.Show("Cannot open 'KTWeeklyNetWorth.dta' to count records.");
PF050:                return;
PF051:              }

PF052:              NONWs = (int)(fk.Length / 47);
PF053:              fk.Close();

PF054:              if (NONWs == 0)
PF055:              {
PF056:                MessageBox.Show("KTWeeklyNetWorth.dta is an Empty File.");
PF057:                return;
PF058:              }

                    // Compute the number of pages to be printed (40 entries per page).
PF059:              PageNo = 0;
PF060:              BigLoop = 0;
PF061:              int TempNONWs = NONWs;

PF062:              while (TempNONWs > 0)
PF063:              {
PF064:                BigLoop++;
PF065:                TempNONWs = TempNONWs - 40;
PF066:              }

PF067:              for (int ww = 0; ww < BigLoop; ww++)
PF068:              {
PF069:                PageNo++;
PF070:                StartPoint = 40 * ww;
PF071:                EndPoint = 40 * (ww + 1);
PF072:                EndPoint > NONWs) EndPoint = NONWs;
PF073:                PrintDocument prndoc = new PrintDocument();
PF074:                prndoc.PrintPage += new PrintPageEventHandler(OnPrintPage);
PF075:                prndoc.Print();
PF076:              }
PF077:            }
//----------------------------------------------------------------------------------------//
PF090:            void OnPrintPage(object obj, PrintPageEventArgs ppea)
PF091:            { // Create the 11 records at the top of the printed page.
PF092:              string[] strRT = new string[]{"KT Weekly Net Worth","Item No",
                    "Year-Week","Total Worth","Total Cost","Gain / Loss","----------",
                    "---------------","---------------","--------------","---------------"};

PF093:              int[] Base3 = new int[] { 660, 0, 400, 800, 1200, 1600, 0, 400, 800,
                                              1200, 1600 };
PF094:              int[] Y3 = new int[] { 200, 300, 300, 300, 300, 300, 365, 365, 365, 365,
                                           365 };
PF095:              int[] Base6 = new int[] { 1270, 0, 800, 1600, 2400, 3200, 0, 800, 1600,
                                              2400, 3200 };
PF096:              int[] Y6 = new int[] { 160, 350, 350, 350, 350, 350, 460, 460, 460, 460,
                                           460 };
PF097:              int[] Data3 = new int[] { 55, 470, 825, 1200, 1600 };
PF098:              int[] Data6 = new int[] { 100, 910, 1650, 2400, 3300 };

PF099:              char[] charWNWBlock = new char[47];

PF100:              Graphics grfx = ppea.Graphics;
PF101:              grfx.PageUnit = GraphicsUnit.Document; // 1/300th of an inch.

                    // This line sets up the printer for 600 DPI vertically.
PF102:              grfx.PageScale = 0.5f; // 1/600th of an inch.

                    // Place the user-selected font into the 'font' queue.
PF103:              Font font1 = new Font(comboBox1.Text, 14);
PF104:              Font font2 = new Font(comboBox1.Text, 10);

PF105:              int ot = 580; // Printing offset.
PF106:              string strWNWBlock;

                    // Re-open file 'KTWeeklyNetWorth.dta'.
                    // IMPORTANT: To read this 'local' file KTWeeklyNetWorth.dta, you must
                    // place the file in the 'PrintAFile' folder, subdirectory 'bin\debug'.
PF107:              StreamReader qq = new StreamReader("KTWeeklyNetWorth.dta");

                    // Print the column headings.
PF108:              if (PageNo == 1)
PF109:                grfx.DrawString(strRT[0], font1, Brushes.Black, Base6[0] + ot, Y6[0]);
PF110:              for (int ss = 1; ss < 11; ss++) // 11 elements of the page header.
PF111:                 grfx.DrawString(strRT[ss], font2, Brushes.Black, Base6[ss] + ot,
                                       Y6[ss]);

                    // All the data to be printed is in record 'strWNWRecord'. The string is
                    // parsed out into 4 substrings: strYearWeek, strTTWorth, strTTCost, and
                    // strTTGainLoss.
PF112:              string[] strData = new string[5];
PF113:              int ItemNo = StartPoint + 1;
PF114:              int LineEntry = 0;
PF115:              for (int kk = StartPoint; kk < EndPoint; kk++) // Up to 40 records.
PF116:              {
PF117:                string strItemNo = (kk + 1).ToString();
PF118:                strData[0] = strItemNo;
PF119:                qq.ReadBlock(charWNWBlock, 0, 47);
PF120:                strWNWBlock = new string(charWNWBlock);
PF121:                strData[1] = strWNWBlock.Substring(5, 5);   // Year-Week.
PF122:                strData[2] = strWNWBlock.Substring(11, 11); // TTWorth.
PF123:                strData[3] = strWNWBlock.Substring(23, 11); // TTCost.
PF124:                strData[4] = strWNWBlock.Substring(35, 11); // TTGainLoss.

PF125:                for (int ww = 0; ww < 5; ww++)              // 5 data items.
PF126:                grfx.DrawString(strData[ww], font2, Brushes.Black, Data6[ww] + ot,
                            580 + 120 * LineEntry);
PF127:                LineEntry++;
PF128:              }
PF129:            } // End of 'OnPrintPage'.
//-----------------------------------------------------------------------------------------//
PF140:            private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
PF141:            { // Combo box.
PF142:              button1.Enabled = true;
PF143:            }
//-----------------------------------------------------------------------------------------//
PF150:            private void button2_Click(object sender, EventArgs e)
PF151:            { // Quit.
PF152:              Close();
PF153:            }
PF154:          }
PF155:        }
//=========================================================================================//
Form1.Designer.cs:
PF170:        namespace PrintAFile
PF171:        {
PF172:          partial class Form1
PF173:          {
                  // Required designer variable.
PF174:            private System.ComponentModel.IContainer components = null;
                  // Clean up any resources being used.
PF175:            protected override void Dispose(bool disposing)
PF176:            {
PF177:              if (disposing && (components != null)) components.Dispose();
PF178:              base.Dispose(disposing);
PF179:            }

PF180:            #region Windows Form Designer generated code
                  // Required method for Designer support.
PF181:            private void InitializeComponent()
PF182:            {
PF183:              this.label1 = new System.Windows.Forms.Label();
PF184:              this.label2 = new System.Windows.Forms.Label();
PF185:              this.button1 = new System.Windows.Forms.Button();
PF186:              this.button2 = new System.Windows.Forms.Button();
PF187:              this.comboBox1 = new System.Windows.Forms.ComboBox();
PF188:              this.SuspendLayout();
                    //
                    // label1
                    //
PF189:              this.label1.AutoSize = true;
PF190:              this.label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 14F,
                      System.Drawing.FontStyle.Underline,System.Drawing.GraphicsUnit.Point,
                      ((byte)(0)));
PF191:              this.label1.Location = new System.Drawing.Point(195, 9);
PF192:              this.label1.Name = "label1";
PF193:              this.label1.Size = new System.Drawing.Size(101, 24);
PF194:              this.label1.TabIndex = 0;
PF195:              this.label1.Text = "Print A File";
                    //
                    // label2
                    //
PF196:              this.label2.AutoSize = true;
PF197:              this.label2.Font = new System.Drawing.Font("Microsoft Sans Serif", 10F,
                      System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point,
                      ((byte)(0)));
PF198:              this.label2.Location = new System.Drawing.Point(76, 51);
PF199:              this.label2.Name = "label2";
PF200:              this.label2.Size = new System.Drawing.Size(115, 17);
PF201:              this.label2.TabIndex = 1;
PF202:              this.label2.Text = "Choose a Font -->";
                    //
                    // button1
                    //
PF203:              this.button1.Location = new System.Drawing.Point(110, 85);
PF204:              this.button1.Name = "button1";
PF205:              this.button1.Size = new System.Drawing.Size(280, 25);
PF206:              this.button1.TabIndex = 2;
PF207:              this.button1.Text = "Print File \'KTWeeklyNetWorth.dta\' in this font";
PF208:              this.button1.Click += new System.EventHandler(this.button1_Click);
                    //
                    // button2
                    //
PF209:              this.button2.Location = new System.Drawing.Point(213, 124);
PF210:              this.button2.Name = "button2";
PF211:              this.button2.Size = new System.Drawing.Size(75, 25);
PF212:              this.button2.TabIndex = 3;
PF213:              this.button2.Text = "Quit";
PF214:              this.button2.Click += new System.EventHandler(this.button2_Click);
                    //
                    // comboBox1
                    //
PF215:              this.comboBox1.Font = new System.Drawing.Font("Microsoft Sans Serif", 10F,
                      System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point,
                      ((byte)(0)));
PF216:              this.comboBox1.FormattingEnabled = true;
PF217:              this.comboBox1.Location = new System.Drawing.Point(204, 52);
PF218:              this.comboBox1.Name = "comboBox1";
PF219:              this.comboBox1.Size = new System.Drawing.Size(121, 24);
PF220:              this.comboBox1.TabIndex = 4;
PF221:              this.comboBox1.SelectedIndexChanged += new
                      System.EventHandler(this.comboBox1_SelectedIndexChanged);
                    //
                    // Form1
                    //
PF222:              this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
PF223:              this.ClientSize = new System.Drawing.Size(484, 162);
PF224:              this.Controls.Add(this.comboBox1);
PF225:              this.Controls.Add(this.button2);
PF226:              this.Controls.Add(this.button1);
PF227:              this.Controls.Add(this.label2);
PF228:              this.Controls.Add(this.label1);
PF229:              this.Name = "Form1";
PF230:              this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
PF231:              this.Text = "Print a File";
PF232:              this.Load += new System.EventHandler(this.Form1_Load);
PF233:              this.ResumeLayout(false);
PF234:              this.PerformLayout();
PF235:            }
PF236:            #endregion

PF237:            private System.Windows.Forms.Label label1;
PF238:            private System.Windows.Forms.Label label2;
PF239:            private System.Windows.Forms.Button button1;
PF240:            private System.Windows.Forms.Button button2;
PF241:            private System.Windows.Forms.ComboBox comboBox1;
PF242:          }
PF243:        }


Chapter 17: Creating, Reading, and Writing Files

This chapter deals with a small number of powerful Visual Studio C# statements: FileStream, StreamReader, and StreamWriter. These statements are defined in using System.IO. We show what you can do with these statements and what you cannot do.

FileStream (Creating a File)

During file creation, most programmers want to ensure that the file open/creation process is successful, so they use the modern try…catch sequence:

FileStream fs;
try
{
  fs = new FileStream ("KTWeeklyNetWorth.dta", FileMode.OpenOrCreate,
                       FileAccess.ReadWrite);
}
catch
{
  MessageBox.Show("Cannot open file "KTWeeklyNetWorth.dta'.");
  return;
}
fs.Close();

At this point in the code, the programmer has a handle to a file stream (fs), but there isn’t much that can be accomplished with it. The length of the file can be determined at this time, which is always helpful:

Int FileLength = fs.Length;

And, as we show in the project CRAndWFiles, there is a read mechanism (see lines CR026-CR039 at the end of this chapter). But all that this read mechanism can read is bytes, not chars nor strings. However, the bytes can be regrouped into chars or strings later (see CR029-CR030). As far as we know, there is no write mechanism supported in this FileStream construct, but this is what FileStream can do for you:

  • Verify that a file is present and openable.

  • Determine the file’s length once it is open.

The file opening sequence is always closed with fs.Close().


StreamReader (Reading a File)

StreamReader is a more streamlined file-reading mechanism, but it does not include the safety mechanism found in FileStream (where the try…catch mechanism alerts the programmer to possible failures in opening a file). However, there is a three-statement sequence that you can place before the StreamReader or StreamWriter statements that searches for the file that should be read or written to. These five lines serve the same purpose as try…catch:

if(!File.Exists("KTWeeklyNetWorth.dta")
  {
    MessageBox.Show("Cannot find file 'KTWeeklyNetWorth.dta'.");
    return;
  }

Having passed this “file exists” test, you can continue with the read or write:

// Begin file opening and reading with 'StreamReader'.
char[] charWNWBlock = new char[47];
// Open existing file 'KTWeeklyNetWorth.dta'.
StreamReader qq = new StreamReader("KTWeeklyNetWorth.dta", Encoding.ASCII);
qq.ReadBlock(charWNWBlock, 0, 47);
string strWNWBlock = new string(charWNWBlock);
qq.Close();

StreamReader has a large number of overloaded constructors. (Pick Index from the Help menu, then type “StreamReader” in the search box; you will find reams of information on this subject.) The argument Encoding.ASCII is the default in reading, but there are so many read formats now that one must be careful not to make any rash assumptions about how a record will be read. (In the Help index, enter “Encoding” and you will see that there are a half-dozen encoding formats.)

There are read mechanisms other than ReadBlock, but this is the most common because most files consist of records that are all the same length. ReadBlock places the record that is read into a char buffer (not string). So it is usually appropriate to place the record into a string immediately. In the case shown above, strWNWBlock is a local variable, but in most cases this variable would be declared at the top of the project as global to the form so the results of the read can be used elsewhere:

public string strWNWBlock;

Once the record is placed into a string, it can immediately be parsed into its individual string variables using the Substring statement:

// Read strShortYW, strYW, strTTWorth, strTTCost, and strTTGainLoss.
wow.ReadBlock(charWNWBlock, 0, 47);
strWNWBlock = new string(charWNWBlock);
strShortYW = strWNWBlock.Substring(0, 4);
strTTWorth = strWNWBlock.Substring(11, 11);
                   etc.


StreamWriter (Writing a File)

StreamWriter also has a large number of overloaded constructors. Whenever you write to a file you must include the encoding format; otherwise, all sorts of bad things can happen. Shown below is a typical StreamWriter sequence:

if(!File.Exists("SpareShort.dta")
{
  MessageBox.Show("Cannot find file 'SpareShort.dta'.");
  return;
}

// Begin file opening and writing with 'StreamWriter'.
// This next line provides a 'string' to write to file 'SpareShort.dta'.
string strWNWBlock = "0444 04-44 22222.22 33333.33 444.44". ToString();
// Open existing file 'ShortSpare.dta' and write to it.
// 'true' below means 'append to file'. 'false' means overwrite.
StreamWriter jj = new StreamWriter("ShortSpare.dta", false, Encoding.ASCII, 47);
jj.Write(strWNWBlock);
jj.Close();


Identifying Read/Write Failures

There are significant differences between StreamReader and StreamWriter. For example, StreamReader has a ReadBlock mechanism that is extremely useful; StreamWriter has no equivalent (but Write may be considered its equivalent since the length of the write buffer is set in the StreamWriter statement; StreamReader has no such buffer size set).

It is also a little strange that, in the case of StreamReader, there is no mention of the size of the read until you reach the ReadBlock() statement. But in StreamWriter the write size is set in the initial StreamWriter construct and nothing need be said about the size in the follow-on statement, Write(). Obviously two different groups of compiler writers fashioned these statements.

Despite having said all of the above, the average programmer should have no problems with file creation, reading, and writing using FileStream, StreamReader, and StreamWriter.

Programmers usually place a source code location in a MessageBox message that indicates when a file is missing or fails to read or write. If you do not place a location in the message and you read and write files in many places in your project, a “failure to read/write” notice may leave you baffled. So we normally write the message this way:

MessageBox.Show("F3L222: Cannot find file SpareShort.dta to read.");

This means the code is located in Form3, line 222. These notices are removed when the project is complete and all is running well. We use the MessageBox as a quasi debug mechanism, as do most other programmers.


Project CRAndWFiles

The project shown below, CRAndWFiles, demonstrates all the file create, read, and write principles mentioned above. The project is located at Visual Studio 2005\Projects\DemosSourceCode\CRAndWFiles. Bring the project into the IDE, then compile and run it. The main window looks like the following:

Image from book
Figure 17-1: Create, Read, and Write Files main window

When the Begin File Opening With 'FileStream’ button is clicked, the button1_Click event handler is actuated. KTWeeklyNetWorth.dta is opened and 47 bytes are read from the file (line CR028). Lines CR029-CR030 convert the bytes to individual chars, which are a part of the charBlockData char array. Line CR031 converts the char array to a string, and CR032 prints that array to show that the read was successful. This method for reading a file is not recommended, but this project shows that it will work!

CRAndWFiles (Creating, Reading, and Writing Files) (CR) Listing

Form1.cs:
CR002:        using System;
CR003:        using System.Collections.Generic;
CR004:        using System.ComponentModel;
CR005:        using System.Data;
CR006:        using System.Drawing;
CR007:        using System.Windows.Forms;
CR008:        using System.IO;     // 'FileStream', 'StreamReader', 'StreamWriter'.
CR009:        using System.Text;   // 'Encoding'.

CR011:        namespace CRAndWFiles
CR012:        {
CR013:          partial class Form1 : Form
CR014:          {
CR015:            public Form1()
CR016:            {InitializeComponent(); }
//-----------------------------------------------------------------------------------------//
CR020:            private void button1_Click(object sender, EventArgs e)
CR021:            { // Begin File Opening With 'FileStream'.
CR022:              FileStream fs;
CR023:              try
CR024:              {
CR025:                fs = new FileStream("WeeklyNetWorth.dta", FileMode.OpenOrCreate,
                               FileAccess.ReadWrite);
                      // Read from the file.
CR026:                byte[] byteBlockData = new byte[47];
CR027:                char[] charBlockData = new char[47];
CR028:                fs.Read(byteBlockData,0,47);
                      // Convert 'byte' to 'char'.
CR029:                for(int mm = 0; mm < 47; mm++)
CR030:                charBlockData[mm] = Convert.ToChar(byteBlockData[mm]);
CR031:                string strBlockData = new string(charBlockData);
CR032:                MessageBox.Show("BlockData read is '" + strBlockData + "' .");
CR033:              }
CR034:              catch
CR035:              {
CR036:                MessageBox.Show("Cannot open file 'WeeklyNetWorth.dta'.");
CR037:                return;
CR038:              }
CR039:            fs.Close();
CR040:            }
//-----------------------------------------------------------------------------------------//
CR050:            private void button2_Click(object sender, EventArgs e)
CR051:            { // Begin File Opening and Reading With 'StreamReader'.
CR052:              char[] charWNWBlock = new char[47];
CR053:              // Open existing file 'WeeklyNetWorth.dta'.
CR054:              // The file must be located in 'CRAandWFiles\bin\debug'.
CR055:              StreamReader qq = new StreamReader("WeeklyNetWorth.dta", Encoding.ASCII);
CR056:              qq.ReadBlock(charWNWBlock, 0, 47);
CR057:              string strWNWBlock = new string(charWNWBlock);
CR058:              MessageBox.Show("Part Two: strWNWBlock = '" + strWNWBlock + "' .");
CR059:              qq.Close();
CR060:            }
//-----------------------------------------------------------------------------------------//
CR070:            private void button3_Click(object sender, EventArgs e)
CR071:            { // Begin File Opening and Writing With 'StreamWriter'.
CR072:              string strWNWBlock = "0444 04-44 22222.22 33333.33 444.44 ".ToString();
                    // Open existing file 'ShortSpare.dta' and write to it.
                    // 'true' below means 'append to file'. 'false' means overwrite.
CR073:              StreamWriter jj = new StreamWriter("ShortSpare.dta",false,
                                      Encoding.ASCII,47);
CR074:              jj.Write(strWNWBlock);
CR075:              MessageBox.Show("Part Three: strWNWBlock = '" + strWNWBlock + "' .");
CR076:              jj.Close();
CR077:            }

When the Begin File Opening and Writing With StreamWriter button is clicked, the button3_Click event handler is actuated. A 47-char string is loaded into strWNWBlock so it can be written to a file named ShortSpare.dta. Line CR073 opens the file and prepares to write ASCII data to it. Line CR074 performs the write operation.

Button 4 is the standard Quit button.

CR080:            private void button4_Click(object sender, EventArgs e)
CR081:            { // Quit.
CR082:              Close();
CR083:            }
CR084:          }
CR085:        }
//=========================================================================================//
Form1.Designer.cs:
CR100:        namespace CRAndWFiles
CR101:        {
CR102:          partial class Form1
CR103:          {
                  // Required designer variable.
CR104:            private System.ComponentModel.IContainer components = null;
                  // Clean up any resources being used.
CR105:            protected override void Dispose(bool disposing)
CR106:            {
CR107:              if (disposing && (components != null)) components.Dispose();
CR108:              base.Dispose(disposing);
CR109:            }

CR110:            #region Windows Form Designer generated code

                  // Required method for Designer support.
CR111:            private void InitializeComponent()
CR112:            {
CR113:              this.button1 = new System.Windows.Forms.Button();
CR114:              this.button2 = new System.Windows.Forms.Button();
CR115:              this.button3 = new System.Windows.Forms.Button();
CR116:              this.button4 = new System.Windows.Forms.Button();
CR117:              this.SuspendLayout();
                    //
                    // button1
                    //
CR118:              this.button1.Location = new System.Drawing.Point(82, 15);
CR119:              this.button1.Name = "button1";
CR120:              this.button1.Size = new System.Drawing.Size(225, 25);
CR121:              this.button1.TabIndex = 0;
CR122:              this.button1.Text = "Begin File Opening With \'FileStream\'";
CR123:              this.button1.Click += new System.EventHandler(this.button1_Click);
                    //
                    // button2
                    //
CR124:              this.button2.Location = new System.Drawing.Point(44, 54);
CR125:              this.button2.Name = "button2";
CR126:              this.button2.Size = new System.Drawing.Size(300, 25);
CR127:              this.button2.TabIndex = 1;
CR128:              this.button2.Text = "Begin File Opening and Reading with
                      \'StreamReader\'";
CR129:              this.button2.Click += new System.EventHandler(this.button2_Click);
                    //
                    // button3
                    //
CR130:              this.button3.Location = new System.Drawing.Point(50, 94);
CR131:              this.button3.Name = "button3";
CR132:              this.button3.Size = new System.Drawing.Size(300, 25);
CR133:              this.button3.TabIndex = 2;
CR134:              this.button3.Text = "Begin File Opening and Writing with
                      \'StreamWriter\'";
CR135:              this.button3.Click += new System.EventHandler(this.button3_Click);
                    //
                    // button4
                    //
CR136:              this.button4.Location = new System.Drawing.Point(162, 133);
CR137:              this.button4.Name = "button4";
CR138:              this.button4.Size = new System.Drawing.Size(75, 25);
CR139:              this.button4.TabIndex = 3;
CR140:              this.button4.Text = "Quit";
CR141:              this.button4.Click += new System.EventHandler(this.button4_Click);
                    //
                    // Form1
                    //
CR142:              this.AutoScaleDimensions = new System.Drawing.SizeF(5F, 13F);
CR143:              this.ClientSize = new System.Drawing.Size(384, 172);
CR144:              this.Controls.Add(this.button4);
CR145:              this.Controls.Add(this.button3);
CR146:              this.Controls.Add(this.button2);
CR147:              this.Controls.Add(this.button1);
CR148:              this.Name = "Form1";
CR149:              this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
CR150:              this.Text = "Create, Read, and Write Files";
CR151:              this.ResumeLayout(false);
CR152:            }
CR153:            #endregion

CR154:            private System.Windows.Forms.Button button1;
CR155:            private System.Windows.Forms.Button button2;
CR156:            private System.Windows.Forms.Button button3;
CR157:            private System.Windows.Forms.Button button4;
CR158:          }


Chapter 18: Menus (Main and Pop-up)

Overview

The presentation of a main menu across the top of most Microsoft products came early in Windows software development in the 1970s. At that time the most significant program that Microsoft sold was the word processor Word.

Word processors were the most complicated piece of software ever invented by man (and they still are), so it was necessary that all user options be presented by a scheme that did not cover or minimize the view of the text on the screen. The main menu with submenu pull-downs was the answer. Later on, context (pop-up) menus appeared in the list of available assets in C and C++ compilers, but these context menus are used infrequently.

Creating menus in the past was a boring and tiresome process. Not so with the menu implementation in Visual Studio C#! You will be pleased at how simple the process has become.

In the first part of this chapter, we create a main menu with submenu items. In the second part, we create a context menu.


A Simple, Programmer-Designed Main Menu

The main menu we create will have the four standard main menu items: File, Edit, Tools, and Help. It will also have three custom top-level entries: Items Received, Items Shipped, and Inventory Reconciliation. We build only the menu — not the code in the event handlers that support each menu item. Under Items Received, there will be three subitems: Catalog Sales Items, Special Sales Items, and Facility Support. Items Shipped will also have three subitems: Awaiting Shipment, Shipped Out, and Items to be Reworked. The third main menu item, Inventory Reconciliation, will have no subitems.

Project MenuMenu

  1. To begin, create a new project in the IDE Visual Studio\Projects folder named MenuMenu. There will be no additional windows; the activity centers on the main window. In the window template, set the main window size at 550 pixels wide by 150 pixels high. For StartPosition, set the window for Center Screen. Set the text in the main window caption as Menu Menu.

  2. Highlight the window template (to fill the Properties window with main window data), click on the Toolbox tab on the left edge of the IDE screen, and find MenuStrip under the Menus & Toolbars category. Click on the MenuStrip icon once, move the mouse pointer over the window template, and click on the template to place the MenuStrip onto the template. A main menu form will appear at the top of the window template, and an icon marked MenuStrip1 will appear at the left bottom of the IDE.

  3. At the top left of the window template there will be a small rectangle with the words “Type here” inside the rectangle. We will use this rectangle to enter the three custom entries later. But first we must place the four standard entries on the menu. Highlight the main menu strip at the top of the window template. Then move to the far left of the strip and right-click until a floating context menu appears. (It is difficult to get this context menu to appear, so be patient.) Click on Insert Standard Items to insert a group of four items (File, Edit, Tools, and Help) in the code.

  4. Next, move to the rectangle with the words “Type here” just to the right of the Help entry that the IDE has inserted into the project. Click on that rectangle and type Items Received. Then click somewhere else on the main window template to examine what you have done so far. Click on Items Received and two more rectangles will appear (one to the right of the Items Received rectangle and one below the item).

    You will quickly learn the rules: If you click on a menu item that has no menu item to its right, the IDE provides a way to include new menu items to the right and also below. If you click on a menu item that already has an item to its right, it provides a way to add items below only.

  5. In the rectangle to the right of Items Received, enter Items Shipped. Click elsewhere on the main window, and then click back on Items Shipped to reveal yet another rectangle to the right. Click on this rectangle and enter the final item on the main menu, Inventory Reconciliation.

  6. Click elsewhere on the main window and examine the seven entries across the top of the window. We will show you later how to correct misspellings and how to add and remove custom items.

  7. To enter the submenu items that accompany the three custom entries you have made, click on Items Received (and the only new rectangle offered will be below the main menu item). Then type in Catalog Sales Items as the first submenu item. Click on the main menu item Items Received again to provide another rectangle for submenu entry, and type Special Sale Items. Click on the Items Received main menu item a third time to produce a third submenu rectangle below, and type Facility Support.

  8. Click on the main menu item Items Shipped to produce a rectangle below (for submenu items). Type the first submenu item Awaiting Shipment, then the second submenu item Shipped Out, and finally the third submenu item Items to be Reworked. This completes all the entries into the fixed main menu.

    Figure 18-1 shows how the main window should look.

    Image from book
    Figure 18-1: Menu Menu window

  9. Move to the main window template and click on each custom menu entry (main menu or submenu item) that is at the end of a menusubmenu “chain.” These are the only entries that should support an event handler. In this case, there are three submenu items under Items Received that need an event handler, three items under Items Shipped, and the main menu item Inventory Reconciliation (a total of seven). To invoke these handlers, click on the menu entry in the main window template to highlight the submenu items below, and then double-click on each submenu item to place its event handler in Form1.cs source code.

If you double-click on a main menu item (such as Items Received) that is not at the end of a menu-submenu chain, the IDE creates a handler to this main menu item even though it should never be used. If you create an unwanted handler for an intermediate menu item, then leave it there. Any attempt to remove the handler will confuse the IDE’s bookkeeping.

Warning

The bookkeeping for the main menu is rather complex, and the first time you attempt to compile a project with a main menu the compiler may “ding” you for not having a handler for an intermediate menu item. For example, the compiler may complain that it cannot find a handler for Items Received. If this occurs, move to the window template, highlight Items Received, and then double-click on it to create this unnecessary handler. This makes the compiler happy, and the project can compile (and eventually run).

Note

Once the source code is presented (to show you that the source code has been entered), you can quickly return to the main window template by pressing the F7 key. This is as far as we take the demonstration — there is no code to place in any of these event handlers.

If you want to assure yourself that the wiring between the menu items and the handler is working as advertised, then create a simple two-line entry within each handler to post the result of a menu item on the screen. For example, in the catalogSalesItemsToolStrip Menu-Item_Click handler enter:

MessageBox.Show("In catalogSalesItemsToolStripMenuItem_Click");
return;

The return statement brings the user back to the MenuMenu main window. If you replace “return” with “Close(),” the project terminates.

Note

The Visual Studio C# version 2.0 compiler is not configured to enter the special code that connects the standard items File, Edit, Tools, and Help to working software. Maybe later.

There are only three tasks that alter the appearance of the fixed menu:

  • Correcting misspellings in the text.

  • Adding another menu or submenu item somewhere along the chain of menu items.

  • Deleting a menu or submenu item.

Correcting Misspellings

For demonstration purposes we will change the word “Catalog” (a submenu item under Items Received) to “Catalogue.”

  1. Right-click on Items Received on the template to open a context menu. Click on Edit DropDown Items to open the following window.

    Image from book
    Figure 18-2: Items Collection Editor

  2. Highlight the catalogSalesItemsToolStripMenuItem entry on the left side of the window. This action displays all the properties of the menu item in the Properties window at the right. Scroll down to the Text entry in the Properties window and change “Catalog” to Catalogue. Close the window by clicking on the OK button at the bottom of the window.

  3. To edit the text in the main menu, click on that item on the main window template. This displays its properties in the Properties window on the IDE screen. Scroll to Text, correct the spelling of the item, and click on another part of the window to “set” the correction. For example, change “Items Shipped” to Items Shipped Out.

Adding a Menu or Submenu Item

To insert a main menu item between two existing main menu items, right-click on the item to the right. A new entry is immediately inserted in the main menu (and you may have to widen the Menu Menu window to provide for the additional item).

For example, add a main menu item Lost Paperwork between Help and Items Received by right-clicking on Items Received. This produces a context menu. Click on Insert, and a new item is inserted. This item already has default text in it, so you must correct the text by highlighting the menu item on the window (to display its properties in the Properties window), scrolling down to the Text entry in the Properties window, and changing the default text to “Lost Paperwork.”

To add an item at the end of the existing far-right item (Inventory Reconciliation), first place the cursor at the right edge of the Inventory Reconciliation entry and click with the mouse. This creates a temporary entry below Inventory Reconciliation. Click again on Inventory Reconciliation to produce a new entry to the right of Inventory Reconciliation. Enter the name of the new menu item, such as the word “HoHoHo,” there. If you desire, you can highlight HoHoHo and place submenu items below it. The temporary entry below Inventory Reconciliation disappears because you did not type any text into it.

Deleting a Menu or Submenu Item

To delete a submenu item, simply highlight that item with the mouse cursor, right-click on the item to reveal a context menu, and select Delete. In this case, highlight the item Shipped Out under Items Shipped and delete it. But what happens to the event handler that was created for Shipped Out? Its code remains in Form1.cs and need not be removed.

Warning

The adding and deleting of both menu and submenu items may cause problems in the source code, so it is best if the programmer waits until all elements of the project have been placed before he creates the main menu.

Compile and run this project. The main window should look like Figure 18-1.

This is as far as we intend to take this example. Note that we have not placed a button on the window template to quit this project, but the white X in the red field at the top right corner of the main window will serve to exit the window.

1.

QUESTION: Suppose I decide to place another main menu item on the far left of the window. Can I insert an item there?

Answers

1.

ANSWER: Yes. Recall that the insert process always inserts to the left of the item selected initially.


A Pop-up Menu (Context Menu)

Most pop-up menus appear only when the user places the mouse pointer on some control or menu item on a window and right-clicks on that item. This is the method we will use to actuate the pop-up context menu you will build.

To place a context menu on the existing main window of project MenuMenu, here is what you do:

  1. Highlight the main window in the window template, move to the Toolbox, and click on the ContextMenuStrip icon under Menus & Toolbars.

  2. Move to the window template and click over it to bring the context menu code into the project. Nothing will show on the main window — only a rectangle will appear at the bottom of the IDE screen with the words “contextMenuStrip1.”

    If you right-click on the contextMenu1 rectangle at the bottom of the IDE, a context menu will appear at the top of the window template (where the main menu was originally). Do not be concerned; you have done nothing wrong. You have not destroyed the main menu created earlier. The IDE is providing a place to enter the context menu items, and the best place (so far as the IDE architects are concerned) is the same place as the main menu.

  3. A context menu usually lists a single column of items from top to bottom (no lower-tier items usually appear to the right of the main column). In the first slot of the context menu, type Jump to the Left; in the space below, type Jump to the Right; in the next space below, type Jump into the Middle; and in the next space below, type And Fight Team, Fight!

There are two questions that remain to be answered in this construct:

  • How does the user actuate this context window?

  • Where are the event handlers for the list of items in the context menu?

The answer to the first question is: There is nothing on the window right now that can act as the catalyst to actuate the context window. The only tool the user has is the mouse. So we will construct a system such that any time the user right-clicks on the main window, the context menu will appear. There are multiple steps in this process:

  1. Highlight the main window template to bring its properties into the Properties window.

  2. Click on the Events icon (the bolt of lightning) at the top of the window to bring the event handlers to the front.

  3. Scroll down the list of event handlers to Mouse and double-click on the MouseUp entry to create an event handler named Form1_MouseUp. It will look like this:

private void Form1_MouseUp(object sender, System.Windows.Forms. MouseEventArgs e)
{

}

  4. Within this new event handler, place this code:

// If the right mouse button was clicked and released,
// display the context menu.
if(e.Button == MouseButtons.Right)
contextMenuStrip1.Show((Control) sender, new Point(e.X, e.Y));
// Point (e.X, e.Y) is the present location of the mouse on the screen.

Note that the reference to “e” above is the argument in the Form1_ MouseUp handler. This code is shown in lines MM037-MM040 of the MenuMenu listing below.

The answer to the second question is: In the window template, click on the contextMenu1 rectangle at the bottom of the IDE to highlight the context menu and display it on the template. Click on each item in the context menu to highlight each entry, and double-click on each to create an event handler. Each time an event handler is created you will be shown the new code in the FormX.cs file.

This completes the discussion of main menus and context menus. A complete listing of MenuMenu follows. The project (in addition to the one you have created) is located at Visual Studio 2005\Projects\DemosSourceCode\MenuMenu. The project you have just created with the same name, MenuMenu, is located at Visual Studio 2005\Projects\ MenuMenu.

MenuMenu (MM) Listing

Form1.cs:
MM002:        using System;
MM003:        using System.Collections.Generic;
MM004:        using System.ComponentModel;
MM005:        using System.Data;
MM006:        using System.Drawing;
MM007:        using System.Windows.Forms;

MM009:        namespace MenuMenu
MM010:        {
MM011:          partial class Form1 : Form
MM012:          {
MM013:            public Form1()
MM014:            {InitializeComponent(); }
//-----------------------------------------------------------------------------------------//
MM020:            private void itemsShippedToolStripMenuItem_Click(object sender, EventArgs e)
MM021:            { }

MM022:            private void catalogSalesToolStripMenuItem_Click(object sender, EventArgs e)
MM023:            { }

MM024:            private void specialSalesToolStripMenuItem_Click(object sender, EventArgs e)
MM025:            { }

MM026:            private void facilitySupportToolStripMenuItem_Click(object sender,
                          EventArgs e)
MM027:            { }

MM028:            private void awaitingShipmentToolStripMenuItem_Click(object sender,
                          EventArgs e)
MM029:            { }

MM030:            private void shippedOutToolStripMenuItem_Click(object sender, EventArgs e)
MM031:            { }

MM032:            private void itemstobeReworkedToolStripMenuItem_Click(object sender,
                          EventArgs e)
MM033:            { }

MM034:            private void inventoryReconciliationToolStripMenuItem_Click(object
                          sender,EventArgs e)
MM035:            { }
//-----------------------------------------------------------------------------------------//
MM036:            private void Form1_MouseUp(object sender, MouseEventArgs e)
MM037:            { // If the right mouse button was clicked and released,
MM038:              // display the context menu.
MM039:              if (e.Button == MouseButtons.Right)
MM040:              contextMenuStrip1.Show((Control)sender, new Point(e.X, e.Y));
                    // Point (e.X, e.Y) is the present location of the 'mouse' on the screen.
MM041:            }
//-----------------------------------------------------------------------------------------//
MM042:            private void jumptotheleftToolStripMenuItem_Click(object sender,
                          EventArgs e)
MM043:            {
MM044:              MessageBox.Show("Jump to the Left");
MM045:              return;
MM046:            }
//-----------------------------------------------------------------------------------------//
MM047:            private void jumptotherightToolStripMenuItem_Click(object sender,
                          EventArgs e)
MM048:            {
MM049:              MessageBox.Show("Jump to the Right");
MM050:              return;
MM051:            }
//-----------------------------------------------------------------------------------------//
MM052:            private void jumpinthemiddleToolStripMenuItem_Click(object sender,
                          EventArgs e)
MM053:            {
MM054:              MessageBox.Show("Jump into the Middle");
MM055:              return;
MM056:            }
//-----------------------------------------------------------------------------------------//
MM057:            private void andfightteamfightToolStripMenuItem_Click(object sender,
                          EventArgs e)
MM058:            {
MM059:              MessageBox.Show("And Fight, Team Fight!");
MM060:              return;
MM061:            }
MM062:          }
MM063:        }
//=========================================================================================//
Form1.Designer.cs:
MM070:        namespace MenuMenu
MM071:        {
MM072:          partial class Form1
MM073:          {
                  // Required designer variable.
MM074:            private System.ComponentModel.IContainer components = null;
                  // Clean up any resources being used.
MM075:            protected override void Dispose(bool disposing)
MM076:            {
MM077:              if (disposing && (components != null)) components.Dispose();
MM078:              base.Dispose(disposing);
MM079:            }

MM080:            #region Windows Form Designer generated code

                  // Required method for Designer support.
MM081:            private void InitializeComponent()
MM082:            {
MM083:              this.components = new System.ComponentModel.Container();
                    System.ComponentModel.ComponentResourceManager resources = new
                      System.ComponentModel.ComponentResourceManager(typeof(Form1));
MM084:              this.menuStrip1 = new System.Windows.Forms.MenuStrip();
MM085:              this.leftRaftingContainer = new System.Windows.Forms.RaftingContainer();

                    [50 lines of code]

MM136:              this.topRaftingContainer.SuspendLayout();
MM137:              ((System.ComponentModel.ISupportInitialize)(this.bottomRaftingContainer)).
MM138:              this.SuspendLayout();
                    //
                    // menuStrip1
                    //

                    [264 lines of code]

                    //
                    // Form1
                    //
MM403:              this.AutoScaleDimensions = new System.Drawing.SizeF(5F, 13F);
MM404:              this.ClientSize = new System.Drawing.Size(492, 116);
MM405:              this.Controls.Add(this.leftRaftingContainer);
MM406:              this.Controls.Add(this.rightRaftingContainer);
MM407:              this.Controls.Add(this.topRaftingContainer);
MM408:              this.Controls.Add(this.bottomRaftingContainer);
MM409:              this.Name = "Form1";
MM410:              this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
MM411:              this.Text = "Menu Menu";
MM412:              this.MouseUp += new
                    System.Windows.Forms.MouseEventHandler(this.Form1_MouseUp);
MM413:              this.menuStrip1.ResumeLayout(false);
MM414:              ((System.ComponentModel.ISupportInitialize)(this.leftRaftingContainer)).
                             EndInit();
MM415:              ((System.ComponentModel.ISupportInitialize)(this.rightRaftingContainer)).
                             EndInit();
MM416:              ((System.ComponentModel.ISupportInitialize)(this.topRaftingContainer)).
                             EndInit();
MM417:              this.topRaftingContainer.ResumeLayout(false);
MM418:              this.topRaftingContainer.PerformLayout();
MM419:              ((System.ComponentModel.ISupportInitialize)(this.bottomRaftingContainer)).
                             EndInit();
MM420:              this.contextMenuStrip1.ResumeLayout(false);
MM421:              this.ResumeLayout(false);
MM422:              this.PerformLayout();
MM423:            }
MM424:            #endregion

MM425:            private System.Windows.Forms.MenuStrip menuStrip1;
MM426:            private System.Windows.Forms.RaftingContainer leftRaftingContainer;

                  [48 lines of code]

MM476:          }
MM477:        }


Chapter 19 : Standard Controls

Overview

The concept of ready-made controls for Windows-class computer programs first appeared in the mid-1980s and blossomed in the 1990s when the controls were standardized by Microsoft. This is about the time that Charles Petzold appeared on the scene. He wrote books that were many hundreds of pages long, explaining all the idiosyncrasies of each type of standard control. His books were necessary to explain to the programming public how to use these wonderful new devices. He does the same thing today, and more.

Once the controls became standardized there was little room for “wildcatting” in the controls business — programmers did everything the Microsoft way. But programmers were relieved of the responsibility of picking and choosing all the details in the presentation and actuation of common controls, and that was worth a lot.

In the early 1990s the first integrated development environment (IDE) suites began to appear. Today you can install the necessary controls in a major Windows program in one afternoon; in the past it took many days. This increase in programmer productivity is assisted by Microsoft’s willingness to package all the code snippets necessary to “tie” a control to the other source code and insert them into the source code at the appropriate places. In effect, the painful lessons taught by Charles Petzold in his computer texts were all mechanized and included in the compiler. No wonder the average compiler grew from about 100 K in size to a 1000 K in 20 years!

With version 2.0 of the Visual Studio C# compiler you will also find more automation. When the compiler finds an error in your code, it will tell you how to fix it. For example, in the old days, if you neglected to “include” a file at the top of the source code that contained the machine code to support a computer statement such as printf(), the error that was written across the bottom of the IDE would simply imply that some form of documentation was missing. It was left to the programmer to figure out what that documentation was. Today’s compiler is much more specific. If you use the pen descriptor DashStyle in your code and forget to include the using file “using System.Drawing.Drawing2D,” the compiler will tell you to place that using statement at the top of your source code to fix the problem.

To the casual observer there remain too many choices among controls — why are there so many ways to ask a user a simple question? The answer is because no compiler manufacturer wants to eliminate any single element of the controls suite, as one never knows if that particular control will come in handy next year. These standard controls will be with us in perpetuity.

Every control is sequenced the same way:

  1. The control presents all options to the user. In some cases the user only gets to pick one option; in others, multiple choices may be made.

  2. The control collects the user response(s) whenever the user indicates that he has made the final choice or choices. Event handlers are placed to shift the program’s focus to a particular group of fol-low-on code as soon as the user has done his job.

  3. The event handler takes over and accomplishes whatever task is desired. When the event handler is finished, the focus may be sent back to the original control (for example, when text checking occurs in a text box), or it may be sent back to the window where the control is located (so the user may decide what to do next). Before a control is exited, the user’s choice(s) must be placed in persistent data locations since those choices disappear as soon as the exit action on the control occurs.

Automation of #1 and #2 above came easily to the integrated development environment, but #3 continues to be the responsibility of the individual programmer.

These are the common controls that will be demonstrated in this chapter:

  • Edit controls

  • Radio buttons

  • Check boxes

  • List boxes

  • Combo boxes (edit box with scrollable text)

  • Group boxes, panels, and radio groups

  • Tab controls

The tab controls are the latest addition to the controls and they serve a useful purpose in most modern programs.


Project StandardControls

These controls will be demonstrated in a project named StandardControls. This project is located at Visual Studio 2005\Projects\Demos-SourceCode\StandardControls. Move to the desktop, and open this project in the IDE. A section of the code in StandardControls is given as each control is discussed.

The main window looks like this:

Image from book
Figure 19-1: Standard Controls window

This main window has a group box (with the text “All”) that must be installed on the window template before any of the radio buttons or the Go! button. All of the controls shown in this chapter are taken from the Toolbox and are transferred to the appropriate template by clicking on the control’s icon in the Toolbox, then moving the mouse pointer over the window template and clicking again.

Once the user has selected an item from the controls list, the Go! button is clicked to move to the proper controls demo. The code for the main window is shown below in lines SC0002-SC0221.

StandardControls (SC) Partial Listing

Form1.cs:
SC0002:       using System;
SC0003:       using System.Collections.Generic;
SC0004:       using System.ComponentModel;
SC0005:       using System.Data;
SC0006:       using System.Drawing;
SC0007:       using System.Windows.Forms;

SC0009:       namespace StandardControls
SC0010:       {
SC0011:         partial class Form1 : Form
SC0012:         {
                  // Create persistent storage for Form2.
SC0013:           public string strForm2EditBox;
SC0014:           public string strForm2RichEditBox;

SC0015:           public Form1()
SC0016:           {InitializeComponent(); }
//-----------------------------------------------------------------------------------------//
SC0020:           private void button1_Click(object sender, EventArgs e)
SC0021:           { // Go!
SC0022:             if(radioButton1.Checked)              // 1. Edit Controls.
SC0023:             { // Create the Form2 modal window.
SC0024:               // 'this' sends Form1 handle to Form2.
SC0025:               Form2 frm2 = new Form2(this);
SC0026:               frm2.ShowDialog();
SC0027:               frm2.Close();
SC0028:             }
SC0029:             else if(radioButton2.Checked)         // 2. Radio Buttons.
SC0030:             { // Create the Form3 modal window.
SC0031:               Form3 frm3 = new Form3();
SC0032:               frm3.ShowDialog();
SC0033:               frm3.Close();
SC0034:             }
SC0035:             else if(radioButton3.Checked)         // 3. Check Boxes.
SC0036:             { // Create the Form4 modal window.
SC0037:               Form4 frm4 = new Form4();
SC0038:               frm4.ShowDialog();
SC0039:               frm4.Close();
SC0040:             }
SC0041:             else if(radioButton4.Checked)         // 4. List Boxes.
SC0042:             { // Create the Form5 modal window.
SC0043:               Form5 frm5 = new Form5();
SC0044:               frm5.ShowDialog();
SC0045:               frm5.Close();
SC0046:             }
SC0047:             else if(radioButton5.Checked)         // 5. Combo Boxes.
SC0048:             { // Create the Form6 modal window.
SC0049:               Form6 frm6 = new Form6();
SC0050:               frm6.ShowDialog();
SC0051:               frm6.Close();
SC0052:             }
SC0053:             else if(radioButton6.Checked)         // 6. Group Boxes.
SC0054:             { // Create the Form7 modal window.
SC0055:               Form7 frm7 = new Form7();
SC0056:               frm7.ShowDialog();
SC0057:               frm7.Close();
SC0058:             }
SC0059:             else if(radioButton7.Checked)         // 7. Tab Controls.
SC0060:             { // Create the Form8 modal window.
SC0061:               Form8 frm8 = new Form8();
SC0062:               frm8.ShowDialog();
SC0063:               frm8.Close();
SC0064:             }
SC0065:           }
//-----------------------------------------------------------------------------------------//
SC0070:           private void button2_Click(object sender, EventArgs e)
SC0071:           { // Quit.
SC0072:             Close();
SC0073:           }
SC0074:         }
SC0075:       }
//=========================================================================================//
Form1.Designer.cs:
SC0090:
SC0091:       namespace StandardControls
SC0092:       {
SC0093:         partial class Form1
SC0094:         {
                  // Required designer variable.
SC0095:           private System.ComponentModel.IContainer components = null;
                  // Clean up any resources being used.
SC0096:           protected override void Dispose(bool disposing)
SC0097:           {
SC0098:             if (disposing && (components != null)) components.Dispose();
SC0099:             base.Dispose(disposing);
SC0100:           }

SC0101:           #region Windows Form Designer generated code
                  // Required method for Designer support.
SC0102:           private void InitializeComponent()
SC0103:           {
SC0104:             this.label1 = new System.Windows.Forms.Label();
SC0105:             this.groupBox1 = new System.Windows.Forms.GroupBox();
SC0106:             this.radioButton1 = new System.Windows.Forms.RadioButton();
SC0107:             this.radioButton2 = new System.Windows.Forms.RadioButton();
SC0108:             this.radioButton3 = new System.Windows.Forms.RadioButton();
SC0109:             this.radioButton4 = new System.Windows.Forms.RadioButton();
SC0110:             this.radioButton5 = new System.Windows.Forms.RadioButton();
SC0111:             this.radioButton6 = new System.Windows.Forms.RadioButton();
SC0112:             this.radioButton7 = new System.Windows.Forms.RadioButton();
SC0113:             this.button1 = new System.Windows.Forms.Button();
SC0114:             this.button2 = new System.Windows.Forms.Button();
SC0115:             this.groupBox1.SuspendLayout();
SC0116:             this.SuspendLayout();
                    //
                    // label1
                    //
SC0117:             this.label1.AutoSize = true;
SC0118:             this.label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 14F,
                      System.Drawing.FontStyle.Underline, System.Drawing.GraphicsUnit.Point,
                      ((byte)(0)));
SC0119:             this.label1.Location = new System.Drawing.Point(90, 16);
SC0120:             this.label1.Name = "label1";
SC0121:             this.label1.Size = new System.Drawing.Size(162, 24);
SC0122:             this.label1.TabIndex = 0;
SC0123:             this.label1.Text = "Standard Controls";
                    //
                    // groupBox1
                    //
SC0124:             this.groupBox1.Controls.Add(this.button1);
SC0125:             this.groupBox1.Controls.Add(this.radioButton7);
SC0126:             this.groupBox1.Controls.Add(this.radioButton6);
SC0127:             this.groupBox1.Controls.Add(this.radioButton5);
SC0128:             this.groupBox1.Controls.Add(this.radioButton4);
SC0129:             this.groupBox1.Controls.Add(this.radioButton3);
SC0130:             this.groupBox1.Controls.Add(this.radioButton2);
SC0131:             this.groupBox1.Controls.Add(this.radioButton1);
SC0132:             this.groupBox1.Font = new System.Drawing.Font("Microsoft Sans Serif", 10F,
                      System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point,
                      ((byte)(0)));
SC0133:             this.groupBox1.Location = new System.Drawing.Point(75, 48);
SC0134:             this.groupBox1.Name = "groupBox1";
SC0135:             this.groupBox1.Size = new System.Drawing.Size(200, 259);
SC0136:             this.groupBox1.TabIndex = 1;
SC0137:             this.groupBox1.TabStop = false;
SC0138:             this.groupBox1.Text = "All";
                    //
                    // radioButton1
                    //
SC0139:             this.radioButton1.AutoSize = true;
SC0140:             this.radioButton1.Location = new System.Drawing.Point(30, 25);
SC0141:             this.radioButton1.Name = "radioButton1";
SC0142:             this.radioButton1.Size = new System.Drawing.Size(122, 21);
SC0143:             this.radioButton1.TabIndex = 0;
SC0144:             this.radioButton1.Text = "1. Edit Controls";

                    [radioButtons 2, 3, 4, 5, 6, 7]

                    //
                    // button1
                    //
SC0185:             this.button1.Location = new System.Drawing.Point(62, 210);
SC0186:             this.button1.Name = "button1";
SC0187:             this.button1.TabIndex = 7;
SC0188:             this.button1.Text = "Go !";
SC0189:             this.button1.Click += new System.EventHandler(this.button1_Click);
                    //
                    // button2
                    //
SC0190:             this.button2.Location = new System.Drawing.Point(48, 318);
SC0191:             this.button2.Name = "button2";
SC0192:             this.button2.TabIndex = 2;
SC0193:             this.button2.Text = "Quit";
SC0194:             this.button2.Click += new System.EventHandler(this.button2_Click);
                    //
                    // Form1
                    //
SC0195:             this.AutoScaleDimensions = new System.Drawing.SizeF(5F, 13F);
SC0196:             this.ClientSize = new System.Drawing.Size(334, 352);
SC0197:             this.Controls.Add(this.button2);
SC0198:             this.Controls.Add(this.groupBox1);
SC0199:             this.Controls.Add(this.label1);
SC0200:             this.Name = "Form1";
SC0201:             this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
SC0203:             this.groupBox1.ResumeLayout(false);
SC0202:             this.Text = "Standard Controls";
SC0204:             this.groupBox1.PerformLayout();
SC0205:             this.ResumeLayout(false);
SC0206:             this.PerformLayout();
SC0207:           }
SC0208:           #endregion

SC0209:           private System.Windows.Forms.Label label1;
SC0210:           private System.Windows.Forms.GroupBox groupBox1;
SC0211:           private System.Windows.Forms.RadioButton radioButton1;
SC0212:           private System.Windows.Forms.RadioButton radioButton2;
SC0213:           private System.Windows.Forms.RadioButton radioButton3;
SC0214:           private System.Windows.Forms.RadioButton radioButton4;
SC0215:           private System.Windows.Forms.RadioButton radioButton5;
SC0216:           private System.Windows.Forms.RadioButton radioButton6;
SC0217:           private System.Windows.Forms.RadioButton radioButton7;
SC0218:           private System.Windows.Forms.Button button1;
SC0219:           private System.Windows.Forms.Button button2;
SC0220:         }
SC0221:       }


Edit Controls

There are two edit controls on the Toolbox: TextBox and RichTextBox. The TextBox cannot be set to a depth (vertically) such that it wraps the text onto a lower line. If the user types too many characters in the TextBox control, the text scrolls to the left and is hidden.

The RichTextBox control can be set in depth (vertically) to accommodate several lines of text. The text that the user enters will wrap onto the next lower line when the upper line is filled.

Image from book
Figure 19-2: Edit Controls window

These edit controls are fully wired with event handlers that take the data entered by the user from the edit control and place it in a persistent location in the main window (frmParent.strForm2EditBox, and frmParent.strForm2RichEditBox, lines SC0332 and SC0342). These variables are declared in Form1, lines SC0013-SC0014. The code for the edit boxes is shown below:

Form2

Form2.cs:
SC0300:       #region Using directives
              [7 lines, same as SC0002 – SC0008]
SC0308:       #endregion

SC0309:       namespace StandardControls
SC0310:       {
SC0311:         partial class Form2 : Form
SC0312:         {
SC0313:           public Form1 frmParent; // Send and receive info to/from Form 1.
SC0314:           public Form2(Form1 frm)
SC0315:           {
SC0316:             frmParent = frm; // Handle to Form1.
SC0317:             InitializeComponent();
SC0318:           }
//-----------------------------------------------------------------------------------------//
SC0320:           private void textBox1_TextChanged(object sender, EventArgs e)
SC0321:           { // Edit box.
                    // This is where you check for correct input.
SC0322:             return;
SC0323:           }
//-----------------------------------------------------------------------------------------//
SC0324:           private void richTextBox1_TextChanged(object sender, EventArgs e)
SC0325:           { // Rich text editbox.
                    // This is where you check for correct input.
SC0326:             return;
SC0327:           }
//-----------------------------------------------------------------------------------------//
SC0330:           private void textBox1_Leave(object sender, EventArgs e)
SC0331:           { // Editbox leave.
                    // Place the 'textBox1.Text' into a persistent variable.
SC0332:             frmParent.strForm2EditBox = textBox1.Text;
SC0333:           }
//-----------------------------------------------------------------------------------------//
SC0340:           private void richTextBox1_Leave(object sender, EventArgs e)
SC0341:           { // Rich textbox leave.
                    // Place the 'richTextBox1.Text' into a persistent variable.
SC0342:             frmParent.strForm2RichEditBox = richTextBox1.Text;
SC0343:           }
//-----------------------------------------------------------------------------------------//
SC0350:           private void button1_Click(object sender, EventArgs e)
SC0351:           { // Quit.
SC0352:             Close();
SC0353:           }
SC0354:         }
SC0355:       }
//=========================================================================================//
Form2.Designer.cs:
SC0400:       namespace StandardControls
SC0401:       {
SC0402:         partial class Form2
SC0403:         {
                  // Required designer variable.
SC0404:           private System.ComponentModel.IContainer components = null;
                  // Clean up any resources being used.
SC0405:           protected override void Dispose(bool disposing)
SC0406:           {
SC0407:             if (disposing && (components != null)) components.Dispose();
SC0408:             base.Dispose(disposing);
SC0409:           }

SC0410:           #region Windows Form Designer generated code
                  // Required method for Designer support.
SC0411:           private void InitializeComponent()
SC0412:           {

                    [3 labels, 1 textBox, 1 richTextBox, 1 button, 60 lines]

SC0473:           }
SC0474:           #endregion

SC0475:           private System.Windows.Forms.Label label1;
SC0476:           private System.Windows.Forms.Label label2;
SC0477:           private System.Windows.Forms.Label label3;
SC0478:           private System.Windows.Forms.TextBox textBox1;
SC0479:           private System.Windows.Forms.RichTextBox richTextBox1;
SC0480:           private System.Windows.Forms.Button button1;
SC0481:         }
SC0482:       }


Radio Buttons

There are two groups of radio buttons presented in the window shown in Figure 19-3. If a radio button is chosen in RadioGroup1, the event handler associated with that button is immediately actuated. When this method is chosen, the user has no time to recover from a mistake (picking the wrong radio button).

Image from book
Figure 19-3: Radio Buttons window

The RadioGroup2 scheme allows the user to pick a button, change her mind and pick a different button, then invoke the proper event handler when the Go! button is clicked.

Since each group of three radio buttons was placed onto a group box, the three buttons act like radio buttons are supposed to: Only one of the group can be selected. If the programmer does not create a group box first (and set each radio button onto that group box), the radio buttons all interact independently, thus defeating the purpose of a list of buttons.

The radio button code is shown below:

Form3

Form3.cs:
SC0500:       #region Using directives
              7 lines, same as SC0002 – SC0008]
SC0508:       #endregion

SC0509:       namespace StandardControls
SC0510:       {
SC0511:         partial class Form3 : Form
SC0512:         {
SC0513:           public Form3()
SC0514:           {
SC0515:             InitializeComponent();
SC0516:           }
//-----------------------------------------------------------------------------------------//
SC0520:           private void radioButton2_CheckedChanged(object sender, EventArgs e)
SC0521:           { // radioButton2.
SC0522:             MessageBox.Show("radioButton2 picked.");
SC0522B:            return;
SC0523:             // Close();
SC0524:           }
//-----------------------------------------------------------------------------------------//
SC0530:           private void radioButton3_CheckedChanged(object sender, EventArgs e)
SC0531:           { // radioButton3.
SC0532:             MessageBox.Show("radioButton3 picked.");
SC0533:             Close();
SC0534:           }
//-----------------------------------------------------------------------------------------//
SC0540:           private void radioButton4_CheckedChanged(object sender, EventArgs e)
SC0541:           { // radioButton4.
SC0542:             MessageBox.Show("radioButton4 picked.");
SC0543:             Close();
SC0544:           }
//-----------------------------------------------------------------------------------------//
SC0550:           private void button1_Click(object sender, EventArgs e)
SC0551:           { // radioGroup2 Go!.
SC0552:             if (radioButton5.Checked)
                    MessageBox.Show("radioButton5 picked.");
SC0553:             else if (radioButton6.Checked)
                    MessageBox.Show("radioButton6 picked.");
SC0554:             else if (radioButton7.Checked)
                    MessageBox.Show("radioButton7 picked.");
SC0555:             Close();
SC0556:           }
//-----------------------------------------------------------------------------------------//
SC0560:           private void button2_Click(object sender, EventArgs e)
SC0561:           { // Quit.
SC0562:             Close();
SC0563:           }
SC0564:         }
SC0565:       }
//========================================================================================//
Form3.Designer.cs:
SC0600:       namespace StandardControls
SC0601:       {
                [1 label, 2 groupBoxes, 6 radioButtons, 2 buttons]
SC0733:       }

The return statement in line SC0522B was placed there to demonstrate the difference between return and Close(): return (line SC0522B) returns to Figure 19-3, and Close() (line SC0523) reverts to the main window, which is shown in Figure 19-1 (the window that created Figure 19-3). We seldom exit a control event handler like this with a return unless it is a text-checking handler.


Check Boxes

Check boxes were created to provide the user the opportunity to select more than one option. Nothing happens until the user clicks on the Continue button at the bottom of the window. The group box is placed on the window template first and the three check boxes are placed onto the group box.

Image from book
Figure 19-4: Check Boxes window

The user can pick any combination of the three check boxes (checked or unchecked, where a checked item has a black check mark in its box, and unchecked is blank). Once the user selections have been made, the Continue button is clicked to invoke the event handler. The code for this window is shown below:

Form4

Form4.cs:
SC0800:       #region Using directives
              [7 lines, same as SC0002 – SC0008]
SC0808:       #endregion

SC0809:       namespace StandardControls
SC0810:       {
SC0811:         partial class Form4 : Form
SC0812:         {
SC0813:           public Form4()
SC0814:           {InitializeComponent(); }
//-----------------------------------------------------------------------------------------//
SC0820:           private void button1_Click(object sender, EventArgs e)
SC0821:           { // Continue button.
SC0822:             if (checkBox1.Checked)
                      MessageBox.Show("You enabled 'Use Larger Font'.");
SC0823:             if (checkBox2.Checked)
                      MessageBox.Show("You enabled 'Print in Portrait Mode'.");
SC0824:             if (checkBox3.Checked)
                      MessageBox.Show("You enabled 'Include Corp. Disclaimer'.");
SC0825:           }
//-----------------------------------------------------------------------------------------//
SC0830:           private void button2_Click(object sender, EventArgs e)
SC0831:           { // Quit.
SC0832:             Close();
SC0833:           }
SC0834:         }
SC0835:       }
//=========================================================================================//
Form4.Designer.cs:
SC0900:       namespace StandardControls
SC0901:       {
                [1 label, 1 groupBox, 3 checkBoxes, 2 buttons]
SC0992:       }


List Boxes

List boxes are provided to the user so she can pick one element in the list and act upon it. The most common use of list boxes is file listings. This is the first control element shown where populating the box may be difficult. Rather than creating a list of files for the list box, we have created a list of songs written and sung by the Beatles, and the user gets to pick one (theoretically to play the song on a PC). The list of songs is in a text file named Beatles.dta. This file must be located in directory StandardControls\bin\debug to be read by the StandardControls project, since the StandardControls executable (*.exe) is placed in this subdirectory by the C# compiler. This file, Beatles.dta, could have been placed elsewhere on the PC hard drive, but the filename in the project would have to include the absolute path to the file, starting with C:.

Image from book
Figure 19-5: List Boxes window

The code that supports this list box is shown below:

Form5

Form5.cs:
SC1000:       #region Using directives
              [7 lines, same as SC0002 – SC0008]
SC1008:       #endregion

SC1009:       namespace StandardControls
SC1010:       {
SC1011:         partial class Form5 : Form
SC1012:         {
SC1013:           public string[] strBeatleSongs = new string[206]; // 206 songs.

SC1014:           public Form5()
SC1015:           {InitializeComponent(); }
//-----------------------------------------------------------------------------------------//
SC1020:           private void Form5_Load(object sender, EventArgs e)
SC1021:           { // Load list items from file 'Beatles.dta'.
                    // This file is located in 'StandardControls\bin\debug'.
SC1022:             StreamReader ww = new StreamReader("Beatles.dta");
SC1023:             int intSongNumber = 0;
SC1024:             int listptr = 0;
SC1025:             char[] dummyChar = new char[1];
SC1026:             char[] charDummyBeatleSong = new char[44];
SC1027:             dummyChar[0]=' ';

SC1028:             while(dummyChar[0] != '@') // '@' shows end of file.
SC1029:             {
SC1030:               listptr = 0;
SC1031:               ww.ReadBlock(dummyChar,0,1);
SC1032:               if(dummyChar[0] == '@') goto Done;
SC1033:               while(dummyChar[0] != ';')
SC1034:               {
SC1035:                 if(dummyChar[0] == '"')
SC1036:                 {}
SC1037:                 else
SC1038:                 {
SC1039:                   charDummyBeatleSong[listptr] = dummyChar[0];
SC1040:                   listptr++;
SC1041:                 }
SC1042:                 ww.ReadBlock(dummyChar,0,1);
SC1043:             }
                    // dummyChar[0] == ';' is the divider between songs.
SC1044:             charDummyBeatleSong[listptr] = '\0';
SC1045:             strBeatleSongs[intSongNumber] = new string(charDummyBeatleSong);

                    // Temporary
                    // if((intSongNumber < 5) || (intSongNumber > 200))
                    // MessageBox.Show("Song number " + intSongNumber.ToString()
                    //+"is"+strBeatleSongs[intSongNumber] + " .");

SC1046:             intSongNumber++;
SC1047:           }
SC1048:           Done:
SC1049:           ww.Close();

SC1050:           for(int jj = 0; jj < 205; jj++)
SC1051:             listBox1.Items.Add(strBeatleSongs[jj]);
SC1052:           }
//-----------------------------------------------------------------------------------------//
SC1060:           private void button1_Click(object sender, EventArgs e)
SC1061:           { // Quit.
                    // See which song the User picked.
SC1062:             MessageBox.Show("The song you picked is: \n '"
                    + strBeatleSongs[listBox1.SelectedIndex] + "'.");
SC1063:             Close();
SC1064:           }
SC1065:         }
SC1066:       }
//=========================================================================================//
Form5.Designer.cs:
SC1100:       namespace StandardControls
SC1101:       {
                [1 label, 1 listBox, 1 button]
SC1150:       }

The “temporary” lines between SC1045 and SC1046 were left in the code to show how we checked the song loading procedure (from the file to string[] strBeatleSongs[]).


Combo Boxes

The ComboBox control is an improvement on the simple ListBox control because it transfers the element in the list that the user chooses to the top part of the control, and more importantly, it conducts a search when the user types characters into the top edit control. The combo box is a combination of an edit box at the top and a list box below.

Image from book
Figure 19-6: Combo Boxes window

While you are in the IDE with the Form6 comboBox highlighted, note that under Property Appearance and DropDownStyle there are three choices: Simple, DropDown, and DropDown List. Unfortunately, none of these three styles allows the programmer to drop down the list as it is being presented to the user; in every case the user must click on the down arrow at the right side of the edit box to make the list visible. This is a mistake — the list should be pre-opened, but Microsoft has never chosen to implement this configuration.

As soon as the user clicks on a song, that song moves up to the edit box. If the user types characters into the edit box, the list will scroll up or down to provide the best possible match to the user’s request. This is a magnificent control; thank goodness the automated code handles all these details.

If the user has picked a song, as soon as the Quit button is clicked, that song name will be shown to the user in a message box. The code supporting this combo box is shown below:

Form6

Form6.cs:
SC1200:       #region Using directives
              [7 lines, same as SC0002 – SC0008]
SC1208:       using System.IO; // 'StreamReader'.

SC1209:       #endregion

SC1210:       namespace StandardControls
SC1211:       {
SC1212:         partial class Form6 : Form
SC1213:         {
SC1214:           public string[] strBeatleSongs = new string[206]; // 206 songs.

SC1215:           public Form6()
SC1216:           {InitializeComponent(); }
//-----------------------------------------------------------------------------------------//
SC1220:           private void Form6_Load(object sender, EventArgs e)
SC1221:           { // Use a 'Simple' combo box.
                    // comboEnumeration.SimpleStyle = ComboBoxStyle.Simple;

                    // Load list items from file 'Beatles.dta'.
                    // This file is located in 'StandardControls\bin\debug'.
SC1222:             StreamReader ww = new StreamReader("Beatles.dta");
SC1223:             int intSongNumber = 0;
SC1224:             int listptr = 0;
SC1225:             char[] dummyChar = new char[1];
SC1226:             char[] charDummyBeatleSong = new char[44];
SC1227:             dummyChar[0]='';

SC1228:             while(dummyChar[0] != '@') // '@' shows end of file.
SC1229:             {
SC1230:               listptr = 0;
SC1231:               ww.ReadBlock(dummyChar,0,1);
SC1232:               if(dummyChar[0] == '@') goto Done;
SC1233:               while(dummyChar[0] != ';')
SC1234:               {
SC1235:                 if(dummyChar[0] == '"')
SC1236:                 {}
SC1237:                 else
SC1238:                 {
SC1239:                   charDummyBeatleSong[listptr] = dummyChar[0];
SC1240:                   listptr++;
SC1241:                 }
SC1242:                 ww.ReadBlock(dummyChar,0,1);
SC1243:               }
                      // dummyChar[0] == ';' is the divider between songs.
SC1244:               charDummyBeatleSong[listptr] = '\0';
SC1245:               strBeatleSongs[intSongNumber] = new string(charDummyBeatleSong);

                      // Temporary
                      // if((intSongNumber < 5) || (intSongNumber > 200))
                      // MessageBox.Show("Song number " + intSongNumber.ToString()
                      //+"is"+strBeatleSongs[intSongNumber] + " .");

SC1246:               intSongNumber++;
SC1247:             }
SC1248:             Done:
SC1249:             ww.Close();

SC1250:             for(int jj = 0; jj < 205; jj++)
SC1251:               comboBox1.Items.Add(strBeatleSongs[jj]);
SC1252:           }
//-----------------------------------------------------------------------------------------//
SC1253:           private void button1_Click(object sender, EventArgs e)
SC1254:           { // Quit.
                    // See which song the User picked.
SC1255:             MessageBox.Show("The song you picked is: \n "
                    + strBeatleSongs[comboBox1.SelectedIndex]);
SC1256:             Close();
SC1257:           }
SC1258:         }
SC1259:       }
//=========================================================================================//
Form6.Designer.cs:
SC1300:       namespace StandardControls
SC1301:       {
                [2 labels, 1 comboBox, 1 button]
SC1359:       }

The “temporary” lines between SC1245 and SC1246 were left in the code to show how we checked the song loading procedure (from the file to string[] strBeatleSongs[]).


Group Boxes

There are only two panels that fit in the background of a window with controls. They are the group box (GroupBox1, shown on the left in Figure 19-7 with three check boxes, and another group box shown on theright, marked RadioGroup1) and the panel, which is shown with a text box inside.

Image from book
Figure 19-7: Group Boxes window

The panel (the panel behind the text box) has three border styles: None, FixedSingle, and Fixed3D. If you set the border style in the panel’s Properties window to None, then the panel disappears from view completely. If you choose FixedSingle, the panel is outlined by a narrow black line. If you chooseFixed3D, you get the sunken panel shown in the following figure.

Note that the group box is used for both radio buttons and check boxes.

This particular window is not equipped with any event handlers.

Form7

Form7.cs:
SC1400:       #region Using directives
              [7 lines, same as SC0002 – SC0008]
SC1408:       #endregion

SC1409:       namespace StandardControls
SC1410:       {
SC1411:         partial class Form7 : Form
SC1412:         {
SC1413:          public Form7()
SC1414:          {InitializeComponent(); }
//-----------------------------------------------------------------------------------------//
SC1420:          private void button1_Click(object sender, EventArgs e)
SC1421:          { // Quit.
SC1422:            Close();
SC1423:          }
SC1424:        }
SC1425:       }
//=========================================================================================//
Form7.Designer.cs:
SC1500:       namespace StandardControls
SC1501:       {
                [1 label, 1 panel, 1 textBox, 2 groupBoxes, 3 checkBoxes,
                          3 radioButtons, 1 button]
SC1634:       }


Tab Controls

The current Microsoft Tab control is much, much simpler to create than the first prototypes of a few years ago. A typical Tab control is shown in Figure 19-8.

Image from book
Figure 19-8: Tab Controls window

These are the steps in the build:

  1. Click on the window form to put the form’s properties into the Properties window. Click on the Tab Control icon, then move the mouse pointer over the window template and click again. The Tab control emerges.

  2. Expand the Tab control rectangle to the dimensions you desire (for this example make it fairly large) by pulling on the handles at the left, bottom, and right of the control.

  3. Highlight the Tab control to bring up its properties in the Properties window. Scroll down to the bottom of the dialog and find the entry Tab Pages. To the right of Tab Pages is the word (Collection). To the right of (Collection) there is a square with three dots on it. Click on the three dots to reveal the TabPage Collection Editor window shown in Figure 19-9.

    Image from book
    Figure 19-9: TabPage Collection Editor window

  4. Highlight tabPage1 of the Members list (or click in the general area of the top of the list), and immediately the properties for tabPage1 will appear in the Properties window. Type the name of the first tab (Albuquerque, NM) in the Text entry area. Go back to the Members list, click on the space below tabPage1, and tabPage2 will appear. Move to the Properties window again and type the name of the second tab into the Text entry area. Keep doing this until all tabs have been named. Press OK to exit the TabPage Collection Editor window.

  5. All the tabs should be present on the Tab control, with the names that you gave them. Click on the first tab (the leftmost tab) to highlight the tab field. The Properties window should display the properties of tabControl1. Open the Toolbox, then click on the RichTextBox icon at the bottom of the list. Move to the Tab control and deposit the RichTextBox control onto the Tab control sheet. Pull the RichTextBox control so it fits nicely on the tab sheet.

  6. Click on the RichTextBox control to bring its properties into the Properties window. Scroll down to Text in the properties list and type the word that should go on the tab sheet. Recall that the RichTextBox control allows multiple lines of code — the TextBox control does not. You must use the RichTextBox. When you are through typing, move the mouse over the Tab control and the text will appear in the control.

  7. Highlight the second tab on the Tab control, move to the Properties window, find Text, and type the words that go into the second tab’s tab sheet.

  8. When you have entered all the words into all the tab sheets, you are finished with the Tab control inputs. Place a label across the top of the Tab control (like Driving Distance (miles) from Denver to:).

  9. To complete the window, add a button to exit.

    The code to support the Tab control is shown below:

    Form8

Form8.cs:
SC1700:       #region Using directives
              [7 lines, same as SC0002 – SC0008]
SC1708:       #endregion

SC1709:       namespace StandardControls
SC1710:       {
SC1711:         partial class Form8 : Form
SC1712:         {
SC1713:           public Form8()
SC1714:           {InitializeComponent(); }
//-----------------------------------------------------------------------------------------//
SC1715:           private void button1_Click(object sender, EventArgs e)
SC1716:           { // Quit.
SC1717:             Close();
SC1718:           }
//-----------------------------------------------------------------------------------------//

SC1719:         }
SC1720:       }
//=========================================================================================//
Form8.Designer.cs:
SC1800:       namespace StandardControls
SC1801:       {
SC1802:         partial class Form8
SC1803:         {
                  // Required designer variable.
SC1804:           private System.ComponentModel.IContainer components = null;

                  // Clean up any resources being used.
SC1805:           protected override void Dispose(bool disposing)
SC1806:           {
SC1807:             if (disposing && (components != null)) components.Dispose();
SC1808:             base.Dispose(disposing);
SC1809:           }

SC1810:           #region Windows Form Designer generated code
                  // Required method for Designer support - do not modify
                  // the contents of this method with the code editor.
SC1811:           private void InitializeComponent()
SC1812:           {
SC1813:             this.label1 = new System.Windows.Forms.Label();
SC1814:             this.label2 = new System.Windows.Forms.Label();
SC1815:             this.tabControl1 = new System.Windows.Forms.TabControl();
SC1816:             this.tabPage1 = new System.Windows.Forms.TabPage();
SC1817:             this.tabPage2 = new System.Windows.Forms.TabPage();
SC1818:             this.button1 = new System.Windows.Forms.Button();
SC1819:             this.tabPage3 = new System.Windows.Forms.TabPage();
SC1820:             this.tabPage4 = new System.Windows.Forms.TabPage();
SC1821:             this.richTextBox1 = new System.Windows.Forms.RichTextBox();
SC1822:             this.richTextBox2 = new System.Windows.Forms.RichTextBox();
SC1823:             this.richTextBox3 = new System.Windows.Forms.RichTextBox();
SC1824:             this.richTextBox4 = new System.Windows.Forms.RichTextBox();
SC1825:             this.tabControl1.SuspendLayout();
SC1826:             this.tabPage1.SuspendLayout();
SC1827:             this.tabPage2.SuspendLayout();
SC1828:             this.tabPage3.SuspendLayout();
SC1829:             this.tabPage4.SuspendLayout();
SC1830:             this.SuspendLayout();
                    //
                    // label1
                    //
SC1831:             this.label1.AutoSize = true;
SC1832:             this.label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 14F,
                      System.Drawing.FontStyle.Underline, System.Drawing.GraphicsUnit.Point,
                      ((byte)(0)));
SC1833:             this.label1.Location = new System.Drawing.Point(140, 16);
SC1834:             this.label1.Name = "label1";
SC1835:             this.label1.Size = new System.Drawing.Size(117, 24);
SC1836:             this.label1.TabIndex = 0;
SC1837:             this.label1.Text = "Tab Controls";
                    //
                    // label2
                    //
SC1838:             this.label2.AutoSize = true;
SC1839:             this.label2.Font = new System.Drawing.Font("Microsoft Sans Serif", 10F,
                      System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point,
                      ((byte)(0)));
SC1840:             this.label2.Location = new System.Drawing.Point(23, 54);
SC1841:             this.label2.Margin = new System.Windows.Forms.Padding(3, 3, 3, 1);
SC1842:             this.label2.Name = "label2";
SC1843:             this.label2.Size = new System.Drawing.Size(221, 17);
SC1844:             this.label2.TabIndex = 1;
SC1845:             this.label2.Text = "Driving Distance (miles), Denver to:";
                    //
                    // tabControl1
                    //
SC1846:             this.tabControl1.Controls.Add(this.tabPage1);
SC1847:             this.tabControl1.Controls.Add(this.tabPage2);
SC1848:             this.tabControl1.Controls.Add(this.tabPage3);
SC1849:             this.tabControl1.Controls.Add(this.tabPage4);
SC1850:             this.tabControl1.Location = new System.Drawing.Point(21, 73);
SC1851:             this.tabControl1.Margin = new System.Windows.Forms.Padding(3, 1, 3, 1);
SC1852:             this.tabControl1.Name = "tabControl1";
SC1853:             this.tabControl1.SelectedIndex = 0;
SC1854:             this.tabControl1.Size = new System.Drawing.Size(376, 136);
SC1855:             this.tabControl1.TabIndex = 2;
                    //
                    // tabPage1
                    //
SC1856:             this.tabPage1.Controls.Add(this.richTextBox1);
SC1857:             this.tabPage1.Location = new System.Drawing.Point(4, 22);
SC1858:             this.tabPage1.Name = "tabPage1";
SC1859:             this.tabPage1.Padding = new System.Windows.Forms.Padding(3);
SC1860:             this.tabPage1.Size = new System.Drawing.Size(368, 110);
SC1861:             this.tabPage1.TabIndex = 0;
SC1862:             this.tabPage1.Text = "Albuquerque, NM";
                    //
                    // tabPage2
                    //
SC1863:             this.tabPage2.Controls.Add(this.richTextBox2);
SC1864:             this.tabPage2.Location = new System.Drawing.Point(4, 22);
SC1865:             this.tabPage2.Name = "tabPage2";
SC1866:             this.tabPage2.Padding = new System.Windows.Forms.Padding(3);
SC1867:             this.tabPage2.Size = new System.Drawing.Size(368, 110);
SC1868:             this.tabPage2.TabIndex = 1;
SC1869:             this.tabPage2.Text = "Kansas City, MO";
                    //
                    // button1
                    //
SC1870:             this.button1.Font = new System.Drawing.Font("Microsoft Sans Serif", 10F,
                      System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point,
                      ((byte)(0)));
SC1871:             this.button1.Location = new System.Drawing.Point(176, 230);
SC1872:             this.button1.Name = "button1";
SC1873:             this.button1.Size = new System.Drawing.Size(75, 25);
SC1874:             this.button1.TabIndex = 3;
SC1875:             this.button1.Text = "Quit";
SC1876:             this.button1.Click += new System.EventHandler(this.button1_Click);
                    //
                    // tabPage3
                    //
SC1877:             this.tabPage3.Controls.Add(this.richTextBox3);
SC1878:             this.tabPage3.Location = new System.Drawing.Point(4, 22);
SC1879:             this.tabPage3.Name = "tabPage3";
SC1880:             this.tabPage3.Size = new System.Drawing.Size(368, 110);
SC1881:             this.tabPage3.TabIndex = 2;
SC1882:             this.tabPage3.Text = "Billings, MT";
                    //
                    // tabPage4
                    //
SC1883:             this.tabPage4.Controls.Add(this.richTextBox4);
SC1884:             this.tabPage4.Location = new System.Drawing.Point(4, 22);
SC1885:             this.tabPage4.Name = "tabPage4";
SC1886:             this.tabPage4.Size = new System.Drawing.Size(368, 110);
SC1887:             this.tabPage4.TabIndex = 3;
SC1888:             this.tabPage4.Text = "Salt Lake City, UT";
                    //
                    // richTextBox1
                    //
SC1889:             this.richTextBox1.BorderStyle = System.Windows.Forms.BorderStyle.None;
SC1890:             this.richTextBox1.Location = new System.Drawing.Point(4, 7);
SC1891:             this.richTextBox1.Name = "richTextBox1";
SC1892:             this.richTextBox1.Rtf = @"{\rtf1\ansi\ansicpg1252\deff0{\fonttbl{\f0\
                      fnil\fcharset0 Microsoft Sans Serif;}}
                      \viewkind4\uc1\pard\lang1033\f0\fs17 Albuquerque, New Mexico is 439
                      miles south of Denver, on Interstate 25. Driving time is about 8 hours.
                      You will pass through Raton Pass, which in times past was one of the
                      more exciting passes to drive through [until the road was four-laned].
                      Santa Fe is on the route, about 70 miles north of Albuquerque. On the
                      way back to Denver you should visit Los Alamos, the Bandelier National
                      Monument, and Taos.\par
                    } ";

SC1893:             this.richTextBox1.Size = new System.Drawing.Size(358, 106);
SC1894:             this.richTextBox1.TabIndex = 0;
                    //
                    // richTextBox2
                    //
SC1895:             this.richTextBox2.BorderStyle = System.Windows.Forms.BorderStyle.None;
SC1896:             this.richTextBox2.Location = new System.Drawing.Point(4, 10);
SC1897:             this.richTextBox2.Name = "richTextBox2";
SC1898:;            this.richTextBox2.Rtf = @"{\rtf1\ansi\ansicpg1252\deff0{\fonttbl{\f0\
                      fnil\fcharset0 Microsoft Sans Serif;}}
                      \viewkind4\uc1\pard\lang1033\f0\fs17 Kansas City is 605 miles east of
                      Denver, on Interstate 70. Driving time is about 12 hours. About
                      halfway there, you will see the Russell Stover's chocolate factory on
                      the south side of the interstate. Be sure to stop there. Mr. Stover
                      invented the Eskimo Pie in the 1920s in Chicago. You will also pass
                      by Abilene, KS, President Eisenhower's boyhood home. \par
                    } ";

SC1899:             this.richTextBox2.Size = new System.Drawing.Size(361, 98);
SC1900:             this.richTextBox2.TabIndex = 0;
                    //
                    // richTextBox3
                    //
SC1901:             this.richTextBox3.BorderStyle = System.Windows.Forms.BorderStyle.None;
SC1902:             this.richTextBox3.Location = new System.Drawing.Point(1, 7);
SC1903:             this.richTextBox3.Margin = new System.Windows.Forms.Padding(3, 0, 3, 3);
SC1904:             this.richTextBox3.Name = "richTextBox3";
SC1905:             this.richTextBox3.Rtf = @"{\rtf1\ansi\ansicpg1252\deff0{\fonttbl{\f0\
                      fnil\fcharset0 Microsoft Sans Serif;}}
                      \viewkind4\uc1\pard\lang1033\f0\fs17 Billings, Montana is 554 miles
                      north of Denver, on Interstate 25. Driving time is about 11 hours.
                      As soon as you enter Montana, look for the Custer Battlefield, which
                      is adjacent to the freeway. Custer doesn't live there any more. On
                      the way back to Denver, stop in Cheyenne and visit the home of the
                      great-granddaddy of all rodeos, 'Cheyenne Days'.\par
                    } ";

SC1906:             this.richTextBox3.Size = new System.Drawing.Size(365, 92);
SC1907:             this.richTextBox3.TabIndex = 0;
                    //
                    // richTextBox4
                    //
SC1908:             this.richTextBox4.BorderStyle = System.Windows.Forms.BorderStyle.None;
SC1909:             this.richTextBox4.Location = new System.Drawing.Point(-2, 7);
SC1910:             this.richTextBox4.Name = "richTextBox4";
SC1911:             this.richTextBox4.Rtf = @"{\rtf1\ansi\ansicpg1252\deff0{\fonttbl{\f0\
                      fnil\fcharset0 Microsoft Sans Serif;}}
                      \viewkind4\uc1\pard\lang1033\f0\fs17 Salt Lake City is 512 miles west
                      and a little north of Denver, on the west side of the Rocky Mountains.
                      Driving time is about 10 hours. There are two ways to get there: Go
                      north on I-25 to Cheyenne and turn west on I-80. This way you can see
                      'Little America' in western Wyoming. Or you can go west from Denver on
                      I-25 until it quits at Interstate 15 in Utah, and follow 15 north to
                      SLC. This way you follow the Colorado River west after you pass
                      through Glenwood Springs. This is probably the most scenic mountain
                      ride in the USA.\par
                    } ";

SC1912:             this.richTextBox4.Size = new System.Drawing.Size(368, 106);
SC1913:             this.richTextBox4.TabIndex = 0;
                    //
                    // Form8
                    //
SC1914:             this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
SC1915:             this.ClientSize = new System.Drawing.Size(414, 260);
SC1916:             this.Controls.Add(this.button1);
SC1917:             this.Controls.Add(this.tabControl1);
SC1918:             this.Controls.Add(this.label2);
SC1919:             this.Controls.Add(this.label1);
SC1920:             this.Name = "Form8";
SC1921:             this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
SC1922:             this.Text = "Tab Controls";
SC1923:             this.tabControl1.ResumeLayout(false);
SC1924:             this.tabPage1.ResumeLayout(false);
SC1925:             this.tabPage2.ResumeLayout(false);
SC1926:             this.tabPage3.ResumeLayout(false);
SC1927:             this.tabPage4.ResumeLayout(false);
SC1928:             this.ResumeLayout(false);
SC1929:             this.PerformLayout();
SC1930:           }

SC1931:           #endregion

SC1932:           private System.Windows.Forms.Label label1;
SC1933:           private System.Windows.Forms.Label label2;
SC1934:           private System.Windows.Forms.TabControl tabControl1;
SC1935:           private System.Windows.Forms.TabPage tabPage1;
SC1936:           private System.Windows.Forms.TabPage tabPage2;
SC1937:           private System.Windows.Forms.Button button1;
SC1938:           private System.Windows.Forms.TabPage tabPage3;
SC1939:           private System.Windows.Forms.TabPage tabPage4;
SC1940:           private System.Windows.Forms.RichTextBox richTextBox1;
SC1941:           private System.Windows.Forms.RichTextBox richTextBox2;
SC1942:           private System.Windows.Forms.RichTextBox richTextBox3;
SC1943:           private System.Windows.Forms.RichTextBox richTextBox4;
SC1944:         }
SC1945:       }


Chapter 20: Logic Statements, Enabling and Disabling Controls, and Focus

Logic Statements

Logic statements are the only decision-making tools a programmer can use in the code he or she generates. These are powerful statements, and they can be written by the programmer such that they include the user (does the user want to follow path A, path B, or path C) or exclude the user (if x is less than 10, follow path A; else follow path B).

Before we discuss logic statements in general, we should discuss some logic statements that are unique to the Visual Studio C# compiler:

  • File.Exists — This statement allows the programmer to check for the existence of a file. If the file does not exist, some evasive action can be taken.

// Open file 'Masterlist.dta' to write the data.
if(!File.Exists("C:\\Program Files\\KT\\Masterfile.dta"))
{
  MessageBox.Show("Cannot find file 'Masterfile.dta' to write.");
  Close();
}
else

  • The group of bool functions that operate on one member of a char array or an ANSIString (we list the string form only):

    • Char.IsControl(string str, int Index) — /, \, @, &, | .

    • Char.IsSeparator(string str, int Index) — for Unicode only; separates footnotes, chapters, etc.

    • Char.IsWhiteSpace(string str, int Index) — a blank

    • Char.IsPunctuation(string str, int Index) — period, semicolon, colon, asterisk, double or single quotes, question mark, exclamation point

    • Char.IsSymbol(string str, int Index) — All Greek characters, etc.

    • Char.IsDigit(string str, int Index) — 0-9

    • Char.IsNumber(string str, int Index) — 0-9

    • Char.IsLetter(string str, int Index) — a-z, A-Z

    • Char.IsUpper(string str, int Index) — Uppercase character

    • Char.IsLower(string str, int Index) — Lowercase character

    • Char.IsLetterOrDigit(string str, int Index) — Letters a-z or A-Z, digits 0-9

    • Char.IsSurrogate(string str, int Index) — Two 16-bit fields that represent (in Unicode) a character in an international language (like Chinese)

      For example:

if(Char.IsDigit(textBox2.Text[1]))
{}
else NumberOfErrors++;

      Note that in the example above, if textBox2.Text[1] is a digit (0-9), nothing happens. But if that statement is false, the else statement increments the NumberOfErrors (to signal a problem in the data).

  • Be careful about generalizing multiple logic code that defeats your purpose. Take a look at the following statement:

if((ASCIIChar[mm] >= &&a') && (ASCIIChar[mm] <= 'Z'))

    It appears to include the complete alphabet (and only the alphabet). Not true. The proper statement is:

if(((ASCIIChar[mm] >= 'a') && (ASCIIChar[mm] <= 'z')) ||
   ((ASCIIChar[mm] >= 'A') && (ASCIIChar[mm] <= 'Z'))

    because there are characters between the lowercase and uppercase sets. The uppercase letters, A-Z, are assigned hexadecimal (base 16) digits from 41 to 5A. The lowercase letters, a-z, are assigned hexadecimal digits from 61 to 7A. The safest test is this:

if(Char.IsLetter(ASCIIChar[mm]))

  • To pass a single character, the logic statement is:

if(ASCIIChar[mm] == '#') [for example, the character '#']

    To exclude a single character, the logic statement is:

If(ASCIIChar[mm] != '#')

    where “!=” means “not equal.”

  • To exclude a contiguous set of characters (like 0-9), you could write this statement:

if((ASCIIChar[mm] < 0) || (ASCIIChar[mm] > 9)) then do something ....

    where “||” is the “or” symbol.

    But the preferred statement uses the NOT of the above:

If(Char.IsDigit(ASCIIChar[mm]))
{}
else
{
  do something....
}

  • In multiple-level logic statements you should shy away from the NOT (!) statement because you can easily end up creating a logic path that none of the characters can pass through.

  • Perhaps the most famous glitch in logic statements is this one:

if(((Char > '0') && (Char < '9')) && ((Char > 'a') && (Char < 'z')))

    No character will ever satisfy this logic statement because no character can exist between 0-9 and a-z.

  • The code shown below comes from a typical text checker sequence. The desired output is a year-week number — two digits for the year, a dash, then two digits for the week, like “04-26.” This code may be found in the KT series.

    • Char[0] must be a 0, 1, or 2, to account for the years 2000 through 2020.

    • Char[1] must be a digit, 0-9.

    • Char[2] must be a dash (-).

    • Char[3] must be a digit, 0-9.

    • Char[4] must be a digit, 0-9, except when Char[3] is a 5. In this case, Char[4] must be a 0, 1, or 2 (since char[3] and char[4] can be no larger than 52).

This code is executed every time the user types a character into a text box.

BS1701:       private void textBox2_TextChanged(object sender, System.EventArgs e)
BS1702:       { // Year-Week number.
                // Check that chars 0, 1, 3, and 4 are digits from 0 to 9.
                // Char 0 must be a 0, 1, or 2 (for the years 2000 - 2020).
                // Also check that char 2 is a dash (-).
BS1703:         int DP2L = textBox2.Text.Length;
BS1704:         int NumberOfErrors = 0;
BS1705:         if(DP2L == 1)
BS1706:         {
BS1707:           if((textBox2.Text[0] >= '0') && (textBox2.Text[0] <= '2'))
BS1708:           { }
BS1709:           else NumberOfErrors++;
BS1710:         }
BS1711:         else if(DP2L == 2)
BS1712:         {
BS1713:           if(Char.IsDigit(textBox2.Text[1]))
BS1714:           { }
BS1715:           else NumberOfErrors++;
BS1716:         }
BS1717:         else if(DP2L == 3)
BS1718:         {
BS1719:           if(textBox2.Text[2] == '-')
BS1720:           { }
BS1721:           else NumberOfErrors++;
BS1722:         }
BS1723:         else if(DP2L == 4)
BS1724:         {
BS1725:           if((textBox2.Text[3]>='0') && (textBox2.Text[3]<='5'))
BS1726:           { }
BS1727:           else NumberOfErrors++;
BS1728:         }
BS1729:         else if(DP2L == 5)
BS1730:         {
                  // Characters [3] and [4] combined can be no larger than 52.
BS1731:           if(textBox2.Text[3] == '5')
BS1732:           {
BS1733:             if((textBox2.Text[4] >= '0') && (textBox2.Text[4] <= '2'))
BS1734:             { }
BS1735:             else NumberOfErrors++;
BS1736:           }
BS1737:           else
BS1738:           {
BS1739:             if(Char.IsDigit(textBox2.Text[4]))
BS1740:             { }
BS1741:             else NumberOfErrors++;
BS1742:           }
BS1743:         }

BS1744:         if(NumberOfErrors > 0)
BS1745:         {
BS1746:           MessageBox.Show("Improper 'Year-Week' data string. Try again.");
BS1747:           return;
BS1748:         }
BS1749:       }

The return in line BS1747 is extremely important! If an error has occurred in the input data, the focus returns to the editBox that is currently being checked. If there are no errors, this procedure is closed.


Enabling and Disabling Controls

There is one Visual Studio C# statement that enables or disables a control. The controls that are most frequently enabled or disabled are buttons, textBoxes, radioButtons, and checkBoxes:

button1.Enabled = false;

-or-

button1.Enabled = true;

-or-

textBox1.Enabled = false;

-or-

textBox1.Enabled = true;

The user can accomplish nothing with a disabled control. As an example, see the following window from the KT Weekly Data Posting project and note how the programmer was finally able to guide the user through this window to get meaningful results by enabling and disabling controls on the window at the proper times.

The problem with the window in Figure 20-1 is that some users neglected to enter a year in the first text box or a week in the second text box. Instead, they went directly to the Post Current Portfolio Prices and Print Net Worth button.

Image from book
Figure 20-1: KT Weekly Data Posting window

The Post Current Portfolio Prices and Print Net Worth button opened the next window, but there was insufficient data to continue the process because the user had not entered the year and week numbers in the first window.

The user could enter a Price per share -> number, but once he clicked on the Perform Computations on this Stock/Bond/Fund button, the program defaulted. Why? Because the program had no idea which data should be retrieved to calculate the six items from Year-Week to Gain or (Loss). What to do?

It was evident that the Post Current Portfolio Prices and Print Net Worth button in Figure 20-1 had to be disabled initially, then enabled after the user entered proper data in the two text boxes. But how do you know when the user has entered data into both the year and the week text boxes in Figure 20-1?

One proposed solution was to enable the Post Current Portfolio button (Figure 20-1) as soon as the program left textBox 2 (the one where the user enters a week of the year). Is this possible? Can you leave that button in the disabled state and enable it just as the program leaves textBox 2?

The answer is no, because the only mechanism to inform textBox 2 that it was being exited by the user (so the enabled statement for the Post Current Portfolio button could be executed in the Leave event handler) was to click on another control (shift the focus away from textBox 2). The only way to enable the Post Current Portfolio button was to introduce an intermediate pushbutton named Both Numbers Entered. This solved the problem, but it made the users angry that they had to pass through so many checkpoints to navigate the window. Figure 20-3 shows the final arrangement:

Image from book
Figure 20-3: Final arrangement of Weekly Data Posting window 1

When the program is loaded, disable both the Post Current Portfolio Prices and Print Net Worth button and the Both Numbers Entered button:

KTW050:     private void Form1_Load(object sender, System.EventArgs e)
KTW051:     { // Disable two buttons.
KTW052:       button1.Enabled = false; // 'Post Current Portfolio Prices'.
KTW053:       button3.Enabled = false; // 'Both Numbers Entered'.
KTW054:     }

As the user leaves the first text box (textBox 1), enable the Both Numbers Entered button:

KTW101:     private void textBox1_Leave(object sender, System.EventArgs e)
KTW102:     { // Year number.
KTW103:       strTB1 = textBox1.Text;
KTW104:       string strShortYear = strTB1.Substring(2,2);
KTW105:       intYear = Convert.ToInt32(strShortYear); // This is sent to Form2.

KTW106:       button3.Enabled = true; // Enable 'Both Numbers Entered'.
KTW107:     }

Hopefully the user would enter data in the second text box (week), and the Both Numbers Entered button would be enabled and ready to carry on the program. The Both Numbers Entered event handler performs some bookkeeping tasks, then enables the Post Current Portfolio button in line KTW176 below.

KTW170:     private void button3_Click(object sender, System.EventArgs e)
KTW171:     { // Both numbers entered.
              // Create the 'Year-Week' number to place in the data files.
KTW172:       string SubString1 = strTB1.Substring(2,2); // Last 2 digits in calendar year.
KTW173:       strYW = SubString1 + "-" + strTB2;
KTW174:       strShortYW = SubString1 + strTB2;
              // Write 'Year-Week' number to Form1, label11.
KTW175:       label11.Text = "Year-Week Number is '" + strYW + "' .";
KTW176:       button1.Enabled = true; // 'Post Current Portfolio' button.
KTW177:     }

Now the user can click on the Post Current Portfolio button and the programmer who wrote the sequence is pretty much assured that all the information will be present when the user reaches the next window, shown in Figure 20-2.

Image from book
Figure 20-2: Weekly Data Posting window 2


Focus

The principal way for a user to request that a control receive the immediate focus of the operating system is to mouse-click on that control. This works fine most of the time, but sometimes the user expects the programmer to preset the focus to the proper control (especially when the window is first opened). The programmer can set the most probable sequence in which controls will be addressed by the user by carefully setting the TabIndex numbers in all the controls on one window. The system will always choose the lowest numbered tab first.

As an example, the programmer would set the TabIndex (a property of all controls) for the first TextBox control in Figure 20-1 to 0 and the TabIndex for the second TextBox control in Figure 20-1 to 1. When the programmer is creating controls on a window template, the IDE will always assign the first control a TabIndex of 0, the next control a 1, etc. However, if the sequence must change later, the programmer must renumber the TabIndexes in the Properties window while each control is highlighted in the window template. This often alleviates the focus problem — but not always. It is important that no two controls have the same TabIndex number.

A Typical Focus Problem

This section offers a demonstration of a focus problem that presented itself during the creation of a Keeping Track program named KTWDP (a shortened version of KTWeeklyDataPosting).

  1. Enter the IDE and bring project KTWDP from the Visual Studio 2005\Projects\DemosSourceCode\KTWDP folder into the IDE screen.

  2. Compile and run the program.

This program has one minor error that could cause much grief among the users. It goes like this: If the user opens this program and then immediately decides to exit the program, the user is given a run-time error like this:

Image from book
Figure 20-4: Focus problem

Finished programs are not supposed to have these kinds of problems, so how can it be fixed?

Once you have executed KTWDP in the IDE and the main window appears (see Figure 20-1), immediately click on the Return to Main button at the lower right of the window. What happens? You should see the error message in Figure 20-4 (or something similar).

Recycle project KTWDP and look closely at the first window (Figure 20-1). What control in this window has the focus? It’s the text box at the top right of the window (textBox1). As soon as this window comes up, the focus is placed on the first text box, and the program expects the user to type data here. (The cursor is blinking in textBox1, which indicates that the focus is there.)

In this case, however, the user simply clicked on the Return to Main button and the operating system became angry. How can we avoid this problem? Do not allow textBox1 to hold the focus when the window first opens.

How do we do this? Recall that every control has a TabIndex, an integer that is assigned by the IDE as the programmer creates controls on a window template. As the KTWDP project is now configured, textBox1 has a TabIndex of 0, which means that the focus moves to this text box as soon as the window is opened. You can confirm that by bringing the Form1 window template into the center of the IDE and clicking on the top text box (textBox1). This action brings the textBox1 properties into the Properties window, and by scrolling down to Behavior and TabIndex you will see its assigned number is 0.

To alleviate the problem of an error in terminating the project immediately (by clicking on Return to Main), we will switch the TabIndex of textBox1 with another control. For example, while the Form1 window template is in the IDE and the textBox1 control is highlighted, scroll down to the TabIndex and change the number 0 to 8. This means that you now have two controls with a TabIndex of 8, but you are about to fix that. Click on the KT Weekly Data Posting label at the top of the template (to place its properties in the Properties window), and change its TabIndex from 8 to 0.

Note

The only reason we swap two TabIndexes at a time is to make sure that we don’t duplicate a number (the compiler prefers one-of-a-kind TabIndexes for active control). It does not care what TabIndex number is assigned to something like a label. You could just as well have assigned textBox1 a TabIndex of 98 and the second text box a TabIndex of 99.

Once you have swapped the two TabIndexes, recompile the project and run it. As soon as the main window appears, note that the cursor is no longer present in the upper text box. Click on the Return to Main button. The run-time error should not appear this time.

The Tradeoffs

As the project was originally configured, as soon as the main window came up (Figure 20-1) the focus was placed on the upper text box and the user could begin typing immediately. After swapping TabIndexes between the top text box and the label at the top of the window, the focus is on the label and not on the text box. The user must now click on the top text box to type data there. This requires the user to perform one more task in this window.

However, the run-time error will not appear now if the user clicks on the Return to Main button immediately. There is no free lunch in this world. For a complete tagged listing of project KTWDP, see the file named App D -- DemoVC#TaggedList.doc in the Visual Studio 2005\ Tagged KT-Demo Code directory.


Chapter 21: Timers and Clocks

Overview

The process for integrating timers and clocks into modern computer programs has been greatly simplified in the last 10 years because the newer operating systems include the system software necessary to incorporate local time into everyone’s computer. There are only two tasks assigned to PC timekeeping software:

  • Reporting time quickly (called “Now” time) for display on one’s PC screen or for time-stamping programs and files as soon as they are created. Word processors were the first to include time-stamping software, and today it is found in most commercial software packages.

  • Operating a timer that measures elapsed time and informs the user when some benchmark time is reached. Even the simplest handheld portable games sold in toy stores have timers that shut down the game process when some agreed-to time has expired.

Before we examine a program that uses both real time and a timer, we should mention a Microsoft innovation that first appeared in the Visual C# compiler: a Sleep statement. It is not used often, but it has great utility in learning situations when the instructor must insert periods of time in source code to aid the students in the learning process. It also comes in handy in the debug process if the programmer needs to slow down a process to examine all of its ramifications.

The Sleep statement works this way:

using System.Threading;

This places the proper software in the code.

Thread.Sleep(10000);

Sleep for 10 seconds, then proceed.

Sleep does not inhibit any central processing unit (CPU) activity like MessageBox does, so it is useful on special occasions.


Project Clock

The main window (and only window) in project Clock is shown below:

Image from book
Figure 21-1: Clock project

A listing of the code that generates this clock is shown at the end of this section. The complete project is located at Visual Studio 2005\Projects\ DemosSourceCode\Clock.

This is how the project is created in the IDE:

  1. A new project named Clock is created in the IDE and the main window template is set to a size of 600 x 600 (which is about as large as a main window can be, unless it is created as full screen). The Start Position property is set to Center Screen.

  2. This window requires painting, so, while the window template is highlighted, the programmer must move to the Properties window, click on the Events icon, and click on the Appearance | Paint entry to produce an event handler named Form1_Paint in the Form1.cs source code. A second handler is also required. Click on Behavior | Load to produce a Form1_Load event handler in the Form1.cs source code.

  3. A Quit button is placed at the lower-right corner of the window. Its event handler is created by double-clicking on the button, and the statement Close() is placed in that event handler.

  4. With the main window highlighted (and the main window properties present in the Properties window), the Toolbox is selected and the Timer icon is clicked. The window template is then clicked to produce a timer1 rectangle at the bottom of the IDE (showing that the timer is now a part of this project). The timer rectangle is highlighted so its properties will appear in the Properties window. All you have to do to create its default event handler is double-click on the timer1 rectangle at the bottom of the IDE screen.

    Note

    In the timer1 properties, one of the Behavior properties is Interval, and the default setting is 100. We have asked several Microsoft people what this “timer interval = 100” has to do with the “timer.interval” setting in line XC027 of the Clock project, but so far we have received no answer. This timer interval (the one in Behavior properties) has an enabled/disabled setting above the timer interval number, but nothing changes if the item is enabled or disabled, or if the 100 is changed to another value. For now, we will ignore this setting in Behavior properties.

  5. As an added feature (totally apart from clock control), an icon named DateTimePicker is clicked in the Toolbox and brought over to the window template. This places a DateTime window in the upper left of the template (and it works with no further actions by the programmer). This DateTimePicker is not required to create the clock.

Everything else that appears on the clock is painted onto the surface; no more window template work is required.

This is how Clock works:

  1. A timer is placed in the declaration of Form1 (in lines XC025-XC028). Line XC026 ties the timer (which is created in XC025) to event handler timer1_Tick. Line XC027 sets the timer interval at 1000 nanoseconds, causing the timer to emit a tick every 1 second.

    You may want to reset this number from 1000 to 100 and note the effect it has on the clock, since a timer.Interval setting of 100 causes the clock mechanism to sample DateTime.Now at a rate of 10 times per second. This causes the display to shake since the refresh time of the clock is so fast.

    If you reset the timer.Interval number to 10000, the clock mechanism samples DateTime.Now every 10 seconds. This causes the hands of the clock to jump every 10 seconds. Since a timer.Interval number of 100 causes the clock face to jitter, and a timer interval of 10000 causes the clock face to remain stationary for several seconds, then jump to the next position, it appears that the timer.Interval setting of 1000 in line XC027 is best.

    Line XC028 starts the timer. This timer is turned off only when the project is exited.

  2. The computations to create the face of the clock are made in lines XC042-XC046 of the Form1_Load event handler when the window is created. The X and Y coordinates are used in the event handler Form1_Paint later.

  3. Line XC048 invokes DateTime for the first time, giving the Form1_Paint event handler an input set to use the first time the clock operation begins. The program works correctly even if line XC048 is eliminated.

  4. Once the timer starts (and calls event handler timer1_Tick one time a second) the DateTime.Now data is generated fast enough to move the clock hands smoothly. Event handler Form1_Paint is called automatically by the operating system at a rate of about 30 times per second (so the image on the screen will not flicker when viewed by the user).

  5. Inside event handler Form1_Paint these events occur:

    1. Lines XC063-XC067 set up the four colored pens and the Arial font.

    2. Lines XC068-XC070 paint the outer edge of the clock.

    3. Lines XC071-XC072 paint the numbers 12 and 1 through 11 on the clock face.

    4. Lines XC073-XC074 paint an X in the middle of the clock.

    5. Lines XC075-XC079 compute the coordinates of the outer end of the second hand (the end not at the center of the clock).

    6. Lines XC080-XC084 compute the coordinates of the end of the minute hand.

    7. Lines XC085-XC089 compute the coordinates of the end of the hour hand.

    8. Lines XC090-XC092 draw the second, minute, and hour hands (second hand in blue, minute hand in green, and hour hand in red).

  6. Event handler timer1_Tick performs the task of acquiring DateTime.Now and spreading the data out into its individual elements. The Invalidate() statement in line XC115 is extremely important: It commands the repainting of the screen using the latest data. If you turn this statement off, the clock stops.

This particular clock is a “slave” to DateTime.Now. That is, it gets all its data from the PC in which it is working, and makes no effort to compute time on its own. The only time the position of a second, minute, or hour hand changes is when DateTime.Now changes. With the Tick Count described in the next part of this chapter it would be possible to call DateTime.Now one time to get the current time, then compute all clock movements from that point on using tick counts. Or you could devise a program where the user sets the current time manually and starts the clock. But why bother?

Clock (XC) Listing

Form1.cs:
XC002:        using System;
XC003:        using System.Collections.Generic;
XC004:        using System.ComponentModel;
XC005:        using System.Data;
XC006:        using System.Drawing;
XC007:        using System.Windows.Forms;

XC009:        namespace Clock
XC010:        {
XC011:          partial class Form1 : Form
XC012:          {
XC013:            DateTime dt, dtNow;
XC014:            int[] X = new int[12];
XC015:            int[] Y = new int[12];
XC016:            int[] PtX = new int[] { 300, 320, 300, 280 };
XC017:            int[] PtY = new int[] { 240, 260, 280, 260 };
                  // Set center of clock:
XC018:            int CtrPtX = 300;
XC019:            int CtrPtY = 260;
XC020:            int[] NumX = new int[] {280,405,490,520,490,405,285,170,87,50,80,165};
XC021:            int[] NumY = new int[] {10,40,125,240,360,445,475,445,360,240,130,40};
XC022:            string[] strHours = new string[12] {"12","1","2","3","4","5","6","7",
                                                      "8","9","10","11"};

XC023:            public Form1()
XC024:            {
XC025:              Timer timer = new Timer();
XC026:            timer.Tick += new EventHandler(timer1_Tick);
XC027:            timer.Interval = 1000; // 1 sample per second.
XC028:            timer.Start();

XC029:            InitializeComponent();
XC030:          }
//-----------------------------------------------------------------------------------------//
XC040:          private void Form1_Load(object sender, EventArgs e)
XC041:          { // Configure clock face.
                  // Compute coords for clock face outer edge:
XC042:            for(int jj = 0; jj < 12; jj++)
XC043:            { // Angle in radians:
XC044:              double AiR = (15.0 + 30.0 * jj) * 3.1416 / 180.0;
XC045:              X[jj] = 300 + (int) Math.Round((258.8 * Math.Sin(AiR)), 0);
XC046:              Y[jj] = 260 - (int) Math.Round ((258.8 * Math.Cos(AiR)), 0);
XC047:            }
XC048:          dtNow = DateTime.Now;
XC049:          }
//-----------------------------------------------------------------------------------------//
XC060:            private void Form1_Paint(object sender, PaintEventArgs e)
XC061:            { // Paint the window.
XC062:              Graphics grfx = e.Graphics;
XC063:              Pen blackPen = new Pen(Color.Black, 2);
XC064:              Pen bluePen = new Pen(Color.Blue, 3);
XC065:              Pen greenPen = new Pen(Color.Green, 4);
XC066:              Pen redPen = new Pen(Color.Red, 6);
XC067:              Font font1 = new Font("Arial", 24);

                    // Paint the outer edge of the clock.
XC068:              for(int kk = 0; kk < 11; kk++)
XC069:              grfx.DrawLine(blackPen, X[kk], Y[kk], X[kk+1], Y[kk+1]);

XC070:              grfx.DrawLine(blackPen, X[11], Y[11], X[0], Y[0]);

                    // Paint hours: 12,1,2,3,4,5,6,7,8,9,10,11.
XC071:              for(int mm = 0; mm < 12; mm++)
XC072:              grfx.DrawString(strHours[mm],font1,Brushes.Black,NumX[mm],NumY[mm]);

                    // Paint an 'X' in the center of the clock.
XC073:              grfx.DrawLine(blackPen, PtX[0], PtY[0], PtX[2], PtY[2]);
XC074:              grfx.DrawLine(blackPen, PtX[1], PtY[1], PtX[3], PtY[3]);

                    // Paint the second, minute, and hour hands.
                    // Set the second hand.
XC075:              double SHAngle = (dtNow.Second / 60.0) * 6.2832;
XC076:              double RadiansSin = Math.Sin(SHAngle);
XC077:              double RadiansCos = Math.Cos(SHAngle);
XC078:              int SHPX = 300 + (int) Math.Round(250.0 * RadiansSin,0);
XC079:              int SHPY = 260 - (int) Math.Round(250.0 * RadiansCos,0);

                    // Set the minute hand.
XC080:              SHAngle = (dtNow.Minute / 60.0) * 6.2832;
XC081:              RadiansSin = Math.Sin(SHAngle);
XC082:              RadiansCos = Math.Cos(SHAngle);
XC083:              int MHPX = 300 + (int) Math.Round(240.0 * RadiansSin,0);
XC084:              int MHPY = 260 - (int) Math.Round(240.0 * RadiansCos,0);

                    // Set the hour hand.
XC085:              SHAngle = (dtNow.Hour/12.0) * 6.2832 + ((dtNow.Minute/60.0) *
                               6.2832 / 12.0);
XC086:              RadiansSin = Math.Sin(SHAngle);
XC087:              RadiansCos = Math.Cos(SHAngle);
XC088:              int HHPX = 300 + (int) Math.Round(150.0 * RadiansSin,0);
XC089:              int HHPY = 260 - (int) Math.Round(150.0 * RadiansCos,0);

                    // Draw second hand.
XC090:              grfx.DrawLine(bluePen, CtrPtX, CtrPtY, SHPX, SHPY);
                    // Draw minute hand.
XC091:              grfx.DrawLine(greenPen, CtrPtX, CtrPtY, MHPX, MHPY);
                    // Draw hour hand.
XC092:              grfx.DrawLine(redPen, CtrPtX, CtrPtY, HHPX, HHPY);
XC093:            }
//-----------------------------------------------------------------------------------------//
XC100:            private void button1_Click(object sender, EventArgs e)
XC101:            { // Quit.
XC102:              Close();
XC103:            }
//-----------------------------------------------------------------------------------------//
XC110:            private void timer1_Tick(object sender, EventArgs e)
XC111:            { // Get current time.
XC112:              dtNow = DateTime.Now;
XC113:              dtNow = new DateTime(dtNow.Year, dtNow.Month, dtNow.Day, dtNow.Hour,
                                         dtNow.Minute, dtNow.Second);
XC114:              if(dtNow != dt) dt = dtNow;
                    // This next statement is extremely important! Turn it off and
                    // see what happens.
XC115:              Invalidate();
XC116:            }
XC117:          }
XC118:        }
//=========================================================================================//
Form1.Designer.cs:
XC140:        namespace Clock
XC141:        {
XC142:          partial class Form1
XC143:          {
                  // Required designer variable.
XC144:            private System.ComponentModel.IContainer components = null;
                  // Clean up any resources being used.
XC145:            protected override void Dispose(bool disposing)
XC146:            {
XC147:              if (disposing && (components != null)) components.Dispose();
XC148:              base.Dispose(disposing);
XC149:            }

XC150:            #region Windows Form Designer generated code

                  // Required method for Designer support.
XC151:            private void InitializeComponent()
XC152:            {
XC153:              this.components = new System.ComponentModel.Container();
XC154:              this.button1 = new System.Windows.Forms.Button();
XC155:              this.dateTimePicker1 = new System.Windows.Forms.DateTimePicker();
XC156:              this.timer1 = new System.Windows.Forms.Timer(this.components);
XC157:              this.SuspendLayout();
                    //
                    // button1
                    //
XC158:              this.button1.Location = new System.Drawing.Point(476, 483);
XC159:              this.button1.Name = "button1";
XC160:              this.button1.Size = new System.Drawing.Size(75, 25);
XC161:              this.button1.TabIndex = 0;
XC162:              this.button1.Text = "Quit";
XC163:              this.button1.Click += new System.EventHandler(this.button1_Click);
                    //
                    // dateTimePicker1
                    //
XC164:              this.dateTimePicker1.Location = new System.Drawing.Point(2, 3);
XC165:              this.dateTimePicker1.Name = "dateTimePicker1";
XC166:              this.dateTimePicker1.TabIndex = 1;
                    //
                    // timer1
                    //
XC167:              this.timer1.Tick += new System.EventHandler(this.timer1_Tick);
                    //
                    // Form1
                    //
XC168:              this.AutoScaleDimensions = new System.Drawing.SizeF(5F, 13F);
XC169:              this.ClientSize = new System.Drawing.Size(584, 532);
XC170:              this.Controls.Add(this.dateTimePicker1);
XC171:              this.Controls.Add(this.button1);
XC172:              this.Name = "Form1";
XC173:              this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
XC174:              this.Text = "Clock";
XC175:              this.Paint += new System.Windows.Forms.PaintEventHandler
                                  (this.Form1_Paint);
XC176:              this.Load += new System.EventHandler(this.Form1_Load);
XC177:              this.ResumeLayout(false);
XC178:            }
XC179:            #endregion

XC180:            private System.Windows.Forms.Button button1;
XC181:            private System.Windows.Forms.DateTimePicker dateTimePicker1;
XC182:            private System.Windows.Forms.Timer timer1;
XC183:          }
XC184:        }


Project Spiral

The timer created in project Clock is the heart of the clock program because it forces event handler timer1_Tick to actuate 10 times per second and find the current time in DateTime.Now. This same timer device can perform elapsed time computations to trigger other events.

Project Spiral emits a tick every second and causes the spiral on the main window display to increase in size by 10 pixels each time. The complete project is located at Visual Studio Projects\DemosSourceCode\Spiral. Place Spiral in the IDE, compile it, and run it.

This is its main window:

Image from book
Figure 21-2: Spiral project

A listing of the software that generates the spiral timer is shown at the end of this section. This is how the project is created in the IDE:

  1. A new project named Spiral is formed in the IDE and the main window template is set to a size of 400 x 400. The Start Position property is set to Center Screen.

  2. This window requires painting, so, while the window template is highlighted, the programmer must move to the Properties window, click on the Events icon, and click on the Appearance | Paint entry to produce an event handler named Form1_Paint in the Form1.cs source code.

  3. A Quit button is placed at the lower-right corner of the window. Its event handler is created by double-clicking on the button, and the statement Close() is placed in that event handler.

  4. With the main window highlighted (and the main window properties present in the Properties window), the Toolbox is selected and the Timer icon is clicked with the mouse. Then the mouse pointer is moved to the window template and clicked there. This produces a timer1 rectangle at the bottom of the IDE (showing that the timer is now a part of this project). The timer rectangle is highlighted so its properties will appear in the Properties window. All you have to do to create its default event handler is double-click on the timer1 rectangle at the bottom of the IDE screen.

This completes the window template work.

This is how Spiral works:

  1. A timer is placed in the declaration of Form1 (in lines SP019-SP022). Line SP020 ties the timer (which is created in SP019) to event handler timer1_Tick. Line SP021 sets the timer interval at 1000, which means that the event handler timer1_tick will actuate once each second.

  2. Line SP022 starts the timer. This timer is turned off only when the project is exited.

  3. Once the timer starts (and calls event handler timer1_Tick once each second) the event handler (lines SP050-SP058) updates intx and inty, which are used in line SP036. The statement Invalidate(), line SP057, forces the system to compute new data in event handler Form1_paint, and the operating system repaints.

  4. Inside event handler Form1_Paint these events occur:

    1. Line SP034 sets the pen color, which is randomly set.

    2. Lines SP036-SP045 paint the spiral onto the window.

    3. Button1, line SP060, shuts down the program.

Spiral (SP) Listing

Form1.cs:
SP002:        using System;
SP003:        using System.Collections.Generic;
SP004:        using System.ComponentModel;
SP005:        using System.Data;
SP006:        using System.Drawing;
SP007:        using System.Windows.Forms;

SP009:        namespace Spiral
SP010:        {
SP011:          partial class Form1 : Form
SP012:          {
SP013:            public const int intRevs = 10;
SP014:            public int intx = 10;
SP015:            public int inty = 10;
SP016:            public DateTime dt, dtNow;

SP017:            public Form1()
SP018:            {
SP019:              Timer timer = new Timer();
SP020:              timer.Tick += new EventHandler(timer1_Tick);
SP021:              timer.Interval = 1000; // 1 sample per second.
SP022:              timer.Start();
SP023:              InitializeComponent();
SP024:            }
//-----------------------------------------------------------------------------------------//
SP030:            private void Form1_Paint(object sender, PaintEventArgs e)
SP031:            {
SP032:              Graphics grfx = e.Graphics;
SP033:              Random rand = new Random();
SP034:              Pen myPen = new Pen(Color.FromArgb(rand.Next(256), rand.Next(256),
                                                       rand.Next(256)));
SP035:              float fltAngle, fltScale;
SP036:              int intPoints = intRevs*2*(intx + inty);
SP037:              PointF[] aptf = new PointF[intPoints];

SP038:              for (int jj = 0; jj < intPoints; jj++)
SP039:              {
SP040:                fltAngle = (float)(jj*2*Math.PI / (intPoints / intRevs));
SP041:                fltScale=1-(float)jj / intPoints;
SP042:                aptf[jj].X = (float)(intx/2*(1+fltScale * Math.Cos(fltAngle)));
SP043:                aptf[jj].Y = (float)(inty/2*(1+fltScale * Math.Sin(fltAngle)));
SP044:              }
SP045:              grfx.DrawLines(myPen, aptf);
SP046:            }
//-----------------------------------------------------------------------------------------//
SP050:            private void timer1_Tick(object sender, EventArgs e)
SP051:            { // Get current time.
SP052:              dtNow = DateTime.Now;
SP053:              dtNow = new DateTime(dtNow.Year, dtNow.Month, dtNow.Day, dtNow.Hour,
                                         dtNow.Minute, dtNow.Second);
SP054:              if(dtNow != dt) dt = dtNow;
SP055:              intx = intx + 10;
SP056:              inty = inty + 10;
SP057:              Invalidate();
SP058:            }
//-----------------------------------------------------------------------------------------//
SP059:            private void button1_Click(object sender, EventArgs e)
SP060:            { // Quit.
SP061:              Close();
SP062:            }
SP063:          }
SP064:        }
//=========================================================================================//
Form1.Designer.cs:
SP100:        namespace Spiral
SP101:        {
SP102:          partial class Form1
SP103:          {
                  // Required designer variable.
SP104:            private System.ComponentModel.IContainer components = null;
                  // Clean up any resources being used.
SP105:            protected override void Dispose(bool disposing)
SP106:            {
SP107:              if (disposing && (components != null)) components.Dispose();
SP108:              base.Dispose(disposing);
SP109:            }

SP110:            #region Windows Form Designer generated code

                  // Required method for Designer support.
SP111:            private void InitializeComponent()
SP112:            {
SP113:              this.components = new System.ComponentModel.Container();
SP114:              this.timer1 = new System.Windows.Forms.Timer(this.components);
SP115:              this.button1 = new System.Windows.Forms.Button();
SP116:              this.SuspendLayout();
                    //
                    // timer1
                    //
SP117:              this.timer1.Tick += new System.EventHandler(this.timer1_Tick);
                    //
                    // button1
                    //
SP118:              this.button1.Location = new System.Drawing.Point(296, 296);
SP119:              this.button1.Name = "button1";
SP120:              this.button1.TabIndex = 0;
SP121:              this.button1.Text = "Quit";
SP122:              this.button1.Click += new System.EventHandler(this.button1_Click);
                    //
                    // Form1
                    //
SP123:              this.AutoScaleDimensions = new System.Drawing.SizeF(5F, 13F);
SP124:              this.ClientSize = new System.Drawing.Size(384, 332);
SP125:              this.Controls.Add(this.button1);
SP126:              this.Name = "Form1";
SP127:              this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
SP128:              this.Text = "Spiral";
SP129:              this.Paint += new System.Windows.Forms.PaintEventHandler
                                  (this.Form1_Paint);
SP130:              this.ResumeLayout(false);
SP131:            }
SP132:            #endregion

SP133:            private System.Windows.Forms.Timer timer1;
SP134:            private System.Windows.Forms.Button button1;
SP135:          }
SP136:        }


Chapter 22: Utilizing IDE Visual Studio C# Prebuilt Code

Overview

The Toolbox in the Microsoft integrated development environment (IDE) has a collection of prebuilt code that is useful for the programmer under special sets of circumstances, and the code always works correctly the first time (no programmer testing involved). The Toolbox dialogs are shown in the first part of this chapter.

The “special sets of circumstances” include file opening, editing, saving, and printing tasks — tasks that are normally performed by the PC’s operating system or commonly available applications, like word processors. In the printing arena, there is no application that will open a file and print it without passing the file through an intermediate application such as Word or Notepad to format the text in the file before printing. As a result, these prebuilt dialogs (other than the Open File dialog) have limited use in most programming applications.

The common controls are listed in the second part of the chapter, with references to the chapters in which they are discussed. The final section lists the Toolbox controls that are not specifically discussed in this text.


The Dialogs

The dialogs described in this section are as follows:

  • Open File dialog

  • Save File dialog

  • Folder Browser dialog

  • Font dialog

  • Color dialog

  • Print dialog

  • Print Preview dialog

The Open File dialog is the most common prebuilt dialog in the computer software industry. It is used to open a file.

Image from book
Figure 22-1: Open File dialog

The code inside the event handler that opens this dialog looks like this:

PB030:     private void button1_Click(object sender, EventArgs e)
PB031:     { // Open file dialog.
PB032:       OpenFileDialog openFile = new OpenFileDialog();
PB033:       openFile.DefaultExt = "doc";
             // The Filter property requires a search string after the pipe ( | ).
PB034:       openFile.Filter = "Word documents (*.doc) | *.doc";
PB035:       openFile.ShowDialog();
PB036:       if(openFile.FileNames.Length > 0)
PB037:       {
PB038:         foreach(string fileName in openFile.FileNames)
PB039:         {
                 // Insert code here to process the files.
PB040:         }
PB041:       }
PB042:     }

The Save File dialog, as its name suggests, is used to save a file.

Image from book
Figure 22-2: Save File dialog

The code inside the event handler that opens this dialog looks like this:

PB050:     private void button2_Click(object sender, EventArgs e)
PB051:     { // Save file dialog.
PB052:       SaveFileDialog saveFile = new SaveFileDialog();
PB053:       saveFile.ShowDialog();
PB054:     }

The Folder Browser dialog allows the user to browse through a variety of folders.

Image from book
Figure 22-3: Folder Browser dialog

The code inside the event handler that opens the Folder Browser dialog looks like this:

PB060:     private void button3_Click(object sender, EventArgs e)
PB061:     { // Folder browser dialog.
PB062:       FolderBrowserDialog folderBrowser = new FolderBrowserDialog();
PB063:       folderBrowser.ShowDialog();
PB064:     }

A number of font effects including style and size can be set in the Font dialog.

Image from book
Figure 22-4: Font dialog

The code inside the event handler that opens the Font dialog looks like this:

PB070:     private void button4_Click(object sender, EventArgs e)
PB071:     { // Font dialog.
PB072:       FontDialog fontDialog = new FontDialog();
PB073:       fontDialog.ShowDialog();
PB074:     }

The Color dialog lets users select from a number of colors or create their own.

Image from book
Figure 22-5: Color dialog

The code inside the event handler that opens the Color dialog looks like this:

PB080:     private void button5_Click(object sender, EventArgs e)
PB081:     { // Color dialog.
PB082:       ColorDialog colorDialog = new ColorDialog();
PB083:       colorDialog.ShowDialog();
PB084:     }

In the Print dialog, the user sets options to print his document.

Image from book
Figure 22-6: Print dialog

The code inside the event handler that opens this dialog looks like this:

PB090:     private void button6_Click(object sender, EventArgs e)
PB091:     { // Print dialog.
PB092:       PrintDialog printDialog = new PrintDialog();
PB093:       PrintDocument printdoc = new PrintDocument();
PB094:       printDialog.Document = printdoc;
PB095:       printDialog.ShowDialog();

PB096:       if(printDialog.ShowDialog() == DialogResult.OK)
PB097:       {
               // Continue with printing. See Petzold, Programming Microsoft Windows
               // with C#, pg 1030.
PB098:       }
PB099:     }

The Print Preview allows you to see the print output on the screen.

Image from book
Figure 22-7: Print Preview dialog

The code inside the event handler that opens this dialog and prints the page looks like this:

PB160:     private void button7_Click(object sender, EventArgs e)
PB161:     { // Print preview dialog.
             // Print file 'KTWNW.dta' in this font.
             // Open file 'KTWeeklyNetWorth.dta' to count records.
             // IMPORTANT: To read this 'local' file 'KTWeeklyNetWorth.dta', you must
             // place the file in the 'PreBuiltCode' folder, subdirectory 'bin\debug'.
PB162:       FileStream fk;
PB163:       try
PB164:       {
PB165:           fk = new FileStream("KTWeeklyNetWorth.dta",FileMode.Open,FileAccess.Read);
PB166:         }
PB167:         catch
PB168:         {
PB169:           MessageBox.Show("Cannot open 'KTWeeklyNetWorth.dta' to count records.");
PB170:           return;
PB171:         }

PB172:         NONWs = (int) (fk.Length / 47);
PB173:         fk.Close();

PB174:         if(NONWs == 0)
PB175:         {
PB176:           MessageBox.Show("KTWeeklyNetWorth.dta is an Empty File.");
PB177:           return;
PB178:         }

               // Compute the number of pages to be printed (40 entries per page).
PB179:         PageNo = 0;
PB180:         BigLoop = 0;
PB181:         int TempNONWs = NONWs;
PB182:         while(TempNONWs > 0)
PB183:         {
PB184:           BigLoop++;
PB185:           TempNONWs = TempNONWs - 40;
PB186:         }

PB187:         for (int ww = 0; ww < BigLoop; ww++)
PB188:         {
PB189:           PageNo++;
PB190:           StartPoint = 40 * ww;
PB191:           EndPoint = 40 * (ww + 1);
PB192:           if (EndPoint > NONWs) EndPoint = NONWs;
PB193:           PrintDocument printDoc = new PrintDocument();
PB194:           printDoc.PrintPage += new PrintPageEventHandler(OnPrintPage);

PB195:           PrintPreviewDialog printpDialog = new PrintPreviewDialog();
PB196:           printpDialog.Document = printDoc;
PB197:           printpDialog.ShowDialog();
                 // See Petzold, Programming Microsoft Windows with C#, pp 1036 - 1043.
PB198:           }
PB199:         }
PB200:       }
PB201:     }
//-----------------------------------------------------------------------------------------//
PB110:     void OnPrintPage(object obj, PrintPageEventArgs ppea)
           // Part of 'Print Preview'.
PB111:     { // Create the 11 records at the top of the printed page.
PB112:       string[] strRT = new string[]{"KT Weekly Net Worth","Item No","Year-Week",
               "Total Worth","Total Cost","Gain / Loss","----------","---------------",
               "---------------","--------------","---------------"};

PB113:       int[] Base3 = new int[] {660,0,400,800,1200,1600,0,400,800,1200,1600};
PB114:       int[] Y3 = new int[] {200,300,300,300,300,300,365,365,365,365,365};
PB115:       int[] Base6 = new int[] {1270,0,800,1600,2400,3200,0,800,1600,2400,3200};
PB116:       int[] Y6 = new int[] {160,350,350,350,350,350,460,460,460,460,460};
PB117:       int[] Data3 = new int[] {55,470,825,1200,1600};
PB118:       int[] Data6 = new int[] {100,910,1650,2400,3300};

PB119:       char[] charWNWBlock = new char[47];

PB120:       Graphics grfx = ppea.Graphics;
PB121:       grfx.PageUnit = GraphicsUnit.Document; // 1/300th of an inch.

             // This line sets up the printer for 600 DPI vertically.
PB122:       grfx.PageScale = 0.5f; // 1/600th of an inch.

             // Select a font.
PB123:       Font font1 = new Font("Arial", 14);
PB124:       Font font2 = new Font("Arial",10);

PB125:       int ot = 580; // Printing Offset.
PB126:       string strWNWBlock;

             // Re-open file 'KTWeeklyNetWorth.dta'.
             // IMPORTANT: To read this 'local' file KTWeeklyNetWorth.dta, you must
             // place the file in the 'PrintAFile' folder, subdirectory 'bin\debug'.
PB127:       StreamReader qq = new StreamReader("KTWeeklyNetWorth.dta");

             // Print the column headings.
PB128:       if(PageNo == 1)
PB129:       grfx.DrawString(strRT[0],font1,Brushes.Black,Base6[0]+ot,Y6[0]);
PB130:       for(int ss = 1; ss < 11; ss++) // 11 elements of the page header.
PB131:       grfx.DrawString(strRT[ss],font2,Brushes.Black,Base6[ss]+ot,Y6[ss]);

             // All the data to be printed is in record 'strWNWRecord'. The string is
             // parsed out into 4 substrings: strYearWeek, strTTWorth, strTTCost, and
             // strTTGainLoss.
PB132:       string[] strData = new string[5];
PB133:       int ItemNo = StartPoint + 1;
PB134:       int LineEntry = 0;
PB135:       for(int kk = StartPoint; kk < EndPoint; kk++) // Up to 40 records.
PB136:       {
PB137:         string strItemNo = (kk + 1).ToString();
PB138:         strData[0] = strItemNo;
PB139:         qq.ReadBlock(charWNWBlock, 0, 47);
PB140:         strWNWBlock = new string(charWNWBlock);
PB141:         strData[1] = strWNWBlock.Substring(5,5);       // Year-Week.
PB142:         strData[2] = strWNWBlock.Substring(11,11);     // TTWorth.
PB143:         strData[3] = strWNWBlock.Substring(23,11);     // TTCost.
PB144:         strData[4] = strWNWBlock.Substring(35,11);     // TTGainLoss.

PB145:         for(int ww = 0; ww < 5; ww++)                  // 5 data items.
PB146:         grfx.DrawString(strData[ww],font2,Brushes.Black,Data6[ww]+ot,
                               580 + 120 * LineEntry);
PB147:         LineEntry++;
PB148:       }
PB149:     } // End of 'OnPrintPage'.

A complete copy of the source code that supports these dialogs (Open File, Save File, Folder Browser, Font, Color, Print, and Print Preview) is located in project PreBuiltCode at Visual Studio 2005\Projects\ DemosSourceCode and in App D -- DemoVC#TaggedList.doc.


Common Controls

Label, Button, TextBox, CheckBox, RadioButton, GroupBox, Panel, ListBox, ComboBox, TabControl, and RichTextBox controls are discussed in Chapter 19, “Standard Controls.”

The MainMenu and ContextMenu controls are discussed in Chapter 18, “Menus.”

Timers and the DateTimePicker control are discussed in Chapter 21, “Timers and Clocks.”


Other Toolbox Controls

The descriptions of the following controls are paraphrased (and shortened) from the .NET Framework Class Library. They are not demonstrated in this text:

  • LinkLabel control — A window label that can display hyperlinks.

  • PictureBox control — A window picture box for displaying an image. Typically the PictureBox control is used to display graphics from a bitmap, metafile, icon, JPEG, GIF, or PNG file.

  • DataGrid control — A data-bound list that displays the items from data sources in a table. The DataGrid control allows you to select, sort, and edit these items.

  • CheckedListBox control — Displays a list box in which a check box is displayed to the left of each item.

  • ListView control — A list that contains a collection of items that can be displayed using any one of four different views. This control allows you to display a list of items with item text and, optionally, an icon to identify the type of item.

  • TreeView control — Displays a hierarchical collection of labeled items, each represented by a TreeNode.

  • Splitter control — A control that allows the user to resize docked controls.

  • HscrollBar control — A standard Windows horizontal scroll bar.

  • VscrollBar control — A standard Windows vertical scroll bar.

  • TrackBar control — A standard Windows track bar. The track bar is a scrollable control similar to the ScrollBar control.

  • ProgressBar control — A Windows progress bar control. It visually indicates the progress of a lengthy operation.

  • ImageList control — Provides methods to manage a collection of Image objects. It is typically used by other controls, such as the ListView, TreeView, or ToolBar.

  • ToolTip control — A small rectangular pop-up window that displays a brief description of a control’s purpose when the mouse hovers over the control.

  • ToolBar control — Adds a toolbar element to a window.

  • StatusBar control — A Windows status bar control. Typically, this control consists of StatusBarPanel objects, each of which displays text and/or an icon.

  • DomainUpDown control — A Windows up-down control that displays string values.

  • NumericUpDown control — A Windows up-down control that displays numeric values.

  • HelpProvider control — Provides pop-up or online help for controls.

  • ErrorProvider control — Provides a user interface to indicate that a control on a form has an error associated with it.


Chapter 23: Creating a Top-Level Program for Groups of Executables

Most complex project suites begin as a collection of simple programs, and at some point the programmers decide to write a top-level program that executes all the lower-level programs in the suite. To demonstrate how to write such a program, we display the top-level program in Keeping Track, the series of programs written earlier that enable a user to keep track of one’s stocks, bonds, and funds. This is the Entry Point main window:

Image from book
Figure 23-1: Entry Point window

This version of KT Entry Point is located in Visual Studio 2005\Projects\KT Source Code\KTEntryPoint. The project uses data files found in C:\Program Files\KT, a subdirectory that ProgramFiles.msi copied from the companion files (see the Introduction).

The seven buttons perform these tasks:

  • Startup [Add or Delete Portfolio Items] executes the Startup program. This program performs the tasks of describing one’s financial portfolio in a file named Masterfile.dta.

  • Buy or Sold Declarations executes KTBuy_Sold, where all purchases and sales of financial documents are reported.

  • Cash In Portfolio is a record of the cash in one’s portfolio. This amount of cash is added to the value of the stocks and bonds in the portfolio every time a net worth calculation is made.

  • Weekly Data Posting executes KTWeeklyDataPosting. In this program, each item listed in Masterfile emerges and the user is expected to enter the current value of each item. As soon as KTWeeklyDataPosting is complete, it invokes an internal program named NetWorth to declare the value of each item in the portfolio and the portfolio’s total worth.

  • Print or Plot Historical Data prints or plots the net worth of the portfolio or the historical value of each item in the portfolio.

  • Masterfile Realphabetize realphabetizes the Masterfile.dta file so the items presented to the user are in alphabetical order.

  • Copy Data to Floppy copies the accumulated Keeping Track data onto a 1.4 M formatted floppy disk to ensure that no data is ever lost.

A partial listing of the KTEntryPoint program is shown at the end of this section. The complete project is located at Visual Studio 2005\Projects\KT Source Code\KTEntryPoint.

When the user clicks on the Cash In Portfolio button, this window appears:

Image from book
Figure 23-2: Cash Assets in Portfolio window

The user is expected to enter a new cash value into the text box if this value has changed. To return to the main window, the user clicks on the Return button.

Place the project C:\Visual Studio 2005\Projects\KT Source Code\ KTEntryPoint into the IDE, compile it, and run it.

Notice that in line EP008 below, a new “using” statement must be included: using System.Diagnostics.

KTEntryPoint (EP) Listing

Form1.cs:
EP002:     using System;
EP003:     using System.Collections.Generic;
EP004:     using System.ComponentModel;
EP005:     using System.Data;
EP006:     using System.Drawing;
EP007:     using System.Windows.Forms;
EP008:     using System.Diagnostics;         // Start new process.
EP009:     using System.IO;                  // 'FileExists', 'StreamReader'.
EP010:     using System.Text;                // 'Encoding'.

EP012:     namespace KTEntryPoint
EP013:     {
EP014:       partial class Form1 : Form
EP015:       {
EP016:         public string strCIP;         // Cash In Portfolio.

EP017:         public Form1()
EP018:         {InitializeComponent(); }
//-----------------------------------------------------------------------------------------//
EP020:         private void Form1_Load(object sender, EventArgs e)
EP021:         { // Place 'Cash in Portfolio' number on the window.
                 // Open file 'CashInPortfolio.dta'.
                 // if (!File.Exists("C:\\Program Files\\KT\\CashInPortfolio.dta"))
EP022:           if (!File.Exists("CashInPortfolio.dta")) // Local address.
EP023:           {
EP024:             MessageBox.Show("Cannot open file 'CashInPortfolio.dta' to read.");
EP025:           }

EP026:           char[] charCIP = new char[9];
                 // StreamReader wf = new StreamReader(
                 // "C:\\Program Files\\KT\\CashInPortfolio.dta", Encoding.ASCII);
EP027:           StreamReader wf = new StreamReader(
                 "CashInPortfolio.dta", Encoding.ASCII); // Local address.
EP028:           wf.ReadBlock(charCIP, 0, 9);
EP029:           strCIP = new string(charCIP);
EP030:           wf.Close();
EP031:           label5.Text = strCIP;
EP032:         }
//-----------------------------------------------------------------------------------------//
EP040:         private void button1_Click(object sender, EventArgs e)
EP041:         { // 'Startup'.
EP042:           ProcessStartInfo startinfo = new ProcessStartInfo(
                 "C:\\Program Files\\KT\\KTStartup.exe");
EP043:           Process.Start(startinfo);
EP044:         }
//-----------------------------------------------------------------------------------------//
EP050:         private void button2_Click(object sender, EventArgs e)
EP051:         { // Buy or Sold Declarations.
EP052:           ProcessStartInfo startinfo = new ProcessStartInfo(
                 "C:\\Program Files\\KT\\KTBuy_Sold.exe");
EP053:           Process.Start(startinfo);
EP054:         }
//-----------------------------------------------------------------------------------------//
EP060:         private void button3_Click(object sender, EventArgs e)
EP061:         { // Cash in portfolio.
                 // Open Form2.
EP062:           Form2 frm2 = new Form2();
EP063:           frm2.ShowDialog();
EP064:           frm2.Close();
EP065:         }
//-----------------------------------------------------------------------------------------//
EP070:         private void button4_Click(object sender, EventArgs e)
EP071:         { // Weekly Data Posting.
EP072:           ProcessStartInfo startinfo = new ProcessStartInfo(
                 "C:\\Program Files\\KT\\KTWeeklyDataPosting.exe");
EP073:           Process.Start(startinfo);
EP074:         }
//-----------------------------------------------------------------------------------------//
EP080:         private void button5_Click(object sender, EventArgs e)
EP081:         { // Print or plot historical data.
EP082:           ProcessStartInfo startinfo = new ProcessStartInfo(
                 "C:\\Program Files\\KT\\KTPlotter.exe");
EP083:           Process.Start(startinfo);
EP084:         }
//-----------------------------------------------------------------------------------------//
EP090:         private void button6_Click(object sender, EventArgs e)
EP091:         { // Masterfile realphabetize.
EP092:           ProcessStartInfo startinfo = new ProcessStartInfo(
                 "C:\\Program Files\\KT\\KTRealphabetize.exe");
EP093:           Process.Start(startinfo);
EP094:         }
//-----------------------------------------------------------------------------------------//
EP100:         private void button7_Click(object sender, EventArgs e)
EP101:         { // Copy data to floppy.
EP102:           ProcessStartInfo startinfo = new ProcessStartInfo(
                 "C:\\Program Files\\KT\\CopyToFloppy.exe");
EP103:           Process.Start(startinfo);
EP104:         }
//-----------------------------------------------------------------------------------------//
EP110:         private void button8_Click(object sender, EventArgs e)
EP111:         { // Quit.
EP112:           Close();
EP113:         }
EP114:       }
EP115:     }
//=========================================================================================//
Form1.Designer.cs:
[The complete 'tagged code' listing is located in App C -- KTVC#TaggedCode.doc]

//=========================================================================================//
Form2.cs:
#region Using directives
EP400:        using System;
EP401:        using System.Collections.Generic;
EP402:        using System.ComponentModel;
EP403:        using System.Data;
EP404:        using System.Drawing;
EP405:        using System.Text;                      // 'Encoding'.
EP406:        using System.Windows.Forms;
EP407:        using System.IO;                        // 'StreamReader', 'StreamWriter'.
EP408:        #endregion

EP409:        namespace KTEntryPoint
EP410:        {
EP411:          partial class Form2 : Form
EP412:          {
EP413:            public string strCIP, strRB;
EP414:            public int Stringlength;

EP415:            public Form2()
EP416:            {InitializeComponent(); }
//-----------------------------------------------------------------------------------------//
EP420:            private void Form2_Load(object sender, EventArgs e)
EP421:            { // Open file 'CashInPortfolio.dta' and place number on screen.
                    // if (!File.Exists("C:\\Program Files\\KT\\CashInPortfolio.dta"))
EP422:              if (!File.Exists("CashInPortfolio.dta")) // Local address.
EP423:              {
EP424:                MessageBox.Show("Cannot open file 'CashInPortfolio.dta' to read.");
EP425:              }

                    // StreamReader wf = new StreamReader(
                    // "C:\\Program Files\\KT\\CashInPortfolio.dta", Encoding.ASCII);
EP426:              StreamReader wf = new StreamReader(
                 "CashInPortfolio.dta", Encoding.ASCII); // local address.
EP427:           char[] charRB = new char[9];
EP428:           wf.ReadBlock(charRB, 0, 9);
EP429:           strRB = new string(charRB);
EP430:           wf.Close();
EP431:           label3.Text = strRB;
EP432:         }
//-----------------------------------------------------------------------------------------//
EP440:         private void textBox1_TextChanged(object sender, EventArgs e)
EP441:         {
                 // New cash value.
                 // Check that each char is a digit or a decimal point.
EP442:           int NumberOfErrors = 0;
EP443:           Stringlength = textBox1.Text.Length;
EP444:           if (Char.IsDigit(textBox1.Text[Stringlength - 1]) ||
EP445:           textBox1.Text[Stringlength - 1] == '.')
EP446:           { }
EP447:           else NumberOfErrors++;

EP448:           int NumberOfPeriods = 0;
EP449:           for (int mm = 0; mm < Stringlength; mm++)
EP450:           {
EP451:             if (textBox1.Text[mm] == '.') NumberOfPeriods++;
EP452:           }

EP453:           if ((NumberOfPeriods > 1) || (NumberOfErrors > 0))
EP454:           {
EP455:             MessageBox.Show("Not a number: Too many periods, a comma, or
                                    bad digits. Try again.");
EP456:           }
EP457:           return;
EP458:         }
//-----------------------------------------------------------------------------------------//
EP460:         private void textBox1_Leave(object sender, EventArgs e)
EP461:         {
EP462:           int DPF = 0;                         // Decimal point found.
EP463:           int DPPosition = 0;                  // Decimal point position.
EP464:           Stringlength = textBox1.Text.Length;
EP465:           for (int bb = 0; bb < Stringlength; bb++)
EP466:           {
EP467:             if (textBox1.Text[bb] == '.')       // Decimal point found.
EP468:             {
EP469:               DPF++;
EP470:               DPPosition = bb;
EP471:             }
EP472:           }

EP473:           if (DPF == 0) // No decimal point found.
EP474:           {
EP475:             if (Stringlength == 1) strCIP = textBox1.Text + ".00     ";
EP476:             else if (Stringlength == 2) strCIP = textBox1.Text + ".00    ";
EP477:             else if (Stringlength == 3) strCIP = textBox1.Text + ".00   ";
EP478:             else if (Stringlength == 4) strCIP = textBox1.Text + ".00  ";
EP479:             else if (Stringlength == 5) strCIP = textBox1.Text + ".00 ";
EP480:             else if (Stringlength == 6) strCIP = textBox1.Text + ".00";
EP481:           }
EP482:           else                                     // Decimal point found.
EP483:           {
EP484:             if ((Stringlength - DPPosition) == 3) // Two digits after decimal point.
EP485:             {
EP486:               if (Stringlength == 4) strCIP = textBox1.Text + "     ";
EP487:               else if (Stringlength == 5) strCIP = textBox1.Text + "    ";
EP488:               else if (Stringlength == 6) strCIP = textBox1.Text + "   ";
EP489:               else if (Stringlength == 7) strCIP = textBox1.Text + "  ";
EP490:               else if (Stringlength == 8) strCIP = textBox1.Text + "  ";
EP491:             }
EP492:             else if ((Stringlength - DPPosition) == 2) // One digit after decimal
                                                              // point.
EP493:             {
EP494:               if (Stringlength == 3) strCIP = textBox1.Text + "0     ";
EP495:               else if (Stringlength == 4) strCIP = textBox1.Text + "0    ";
EP496:               else if (Stringlength == 5) strCIP = textBox1.Text + "0   ";
EP497:               else if (Stringlength == 6) strCIP = textBox1.Text + "0  ";
EP498:               else if (Stringlength == 7) strCIP = textBox1.Text + "0 ";
EP499:               else if (Stringlength == 8) strCIP = textBox1.Text + "0";
EP500:             }
EP501:             else if ((Stringlength - DPPosition) == 1) // No digits after decimal
                                                              // point.
EP502:             {
EP503:               if (Stringlength == 3) strCIP = textBox1.Text + "00    ";
EP504:               else if (Stringlength == 4) strCIP = textBox1.Text + "00   ";
EP505:               else if (Stringlength == 5) strCIP = textBox1.Text + "00  ";
EP506:               else if (Stringlength == 6) strCIP = textBox1.Text + "00 ";
EP507:               else if (Stringlength == 7) strCIP = textBox1.Text + "00";
EP508:             }
EP509:           }

                 // Open file 'CashInPortfolio.dta'.
                 // if (!File.Exists("C:\\Program Files\\KT\\CashInPortfolio.dta"))
EP510:           if (!File.Exists("CashInPortfolio.dta"))     // local address.
EP511:           {
EP512:             MessageBox.Show("Cannot open file 'CashInPortfolio.dta' to write.");
EP513:           }

                 // 'false' below means 'overwrite'.
                 // StreamWriter wg = new StreamWriter(
                 // "C:\\Program Files\\KT\\CashInPortfolio.dta", false,
                 // Encoding.ASCII, 9);
EP514:           StreamWriter wg = new StreamWriter(
EP515:           "CashInPortfolio.dta", false, Encoding.ASCII, 9); // local address.
EP516:           wg.Write(strCIP);
EP517:           wg.Close();
EP518:         }
//-----------------------------------------------------------------------------------------//
EP520:         private void button1_Click(object sender, EventArgs e)
EP521:         { // Return
EP522:           Close();
EP523:         }
EP524:       }
EP525:     }
//========================================================================================//
Form2.Designer.cs:
[The complete tagged code listing is located in App C -- KTVC#TaggedCode.doc]


Chapter 24: Creating a Project Icon

Overview

The Visual Studio C# version 2.0 compiler does not provide a default icon for every new project created in the IDE. However, it is easy to bring into the project the tools necessary for icon creation. For a demonstration of the processes required to create a project icon, open the IDE and click on the File main menu item, followed by Open and Project/Solution. In the Open Project window, move to Visual Studio 2005\Projects\DemosSourceCode\ProjectIcon. Open this project by clicking on its solution file, ProjectIcon.sln.

Note

There is a similar project, also in DemosSourceCode, named ProjectIconComplete. This project already has the completed project icon embedded in it. You don’t want to bring this project into the IDE unless your attempts at adding an icon to ProjectIcon fail.

Make no changes to project ProjectIcon since the details of the main window have nothing to do with successfully creating a project icon.


Importing a Project Icon

It is almost impossible to import an image from elsewhere and force it into the icon format because the format is so rigid. However, if you have already created a 32 pixel by 32 pixel icon in 16 colors and want to use it in this project, you can retrieve it by highlighting the Form1 window template (to bring its properties into the Properties window), scrolling down in the Properties window to Window Style | Icon, and clicking on the icon in the right column. This opens a small icon at the right edge of the right column with three dots in it (the Browse icon). When you click on the Browse icon, the Open File window appears and you are free to find an icon file (with the extension *.ico) anywhere on your PC. Once you open the icon file (by double-clicking on the filename), the IDE places that file into your project. However, the chances of importing an icon into your project that is acceptable to C# version 2.0 are slim. Besides, it is easy to create a new icon with the tools that Microsoft provides in the IDE.


Creating a Project Icon from Scratch

To bring the tools necessary to create a project icon into project ProjectIcon, do the following:

  1. Click on Project on the main menu. In the pop-up menu that appears, click on Add New Item. The following window opens:

    Image from book
    Figure 24-1: Add New Item window

  2. Find Icon File in the list and double-click on it. The IDE places a new file in the project, named Icon1.ico. This icon file is immediately displayed in the center of the IDE. The icon template is a 32 pixel by 32 pixel area.

  3. The default magnification for the icon is 6X. You can increase the icon’s size to 8X by clicking on the magnifying glass on the toolbar at the top of the IDE (the second row of tools below the main menu). When you click on the down arrow to the right of the magnifying glass, you are presented with magnifications of 1, 2, 6, and 8. Choose 8 to maximize the size of the icon in the IDE.

Note

Perform all painting exercises on the icon template on the right side of the Icon Editor. The small icon on the left side has other uses.

You are now ready to proceed with project ProjectIcon.


Project ProjectIcon

It is time to color the icon, using the tools in the Icon Editor. At this point, you have a 32 by 32 area in which to create the icon, but no colors to work with. If there is no color palette displayed at the far left of the IDE, you must bring one into the IDE.

  1. Right-click on the Image item on the main menu. Click on the Show Colors Window to place the color palette into the IDE (you only have to do this once in the lifetime of the compiler).

  2. There are many tools presented in the colors toolbar at the top of the IDE (two rows below the main menu), but the tool that you need right now is the pencil tool, which is the first icon to the right of the magnifying glass. Click on the pencil tool so you can color the pixels in the icon one at a time. (Or you can drop the pencil tool on a particular pixel and hold the left mouse button down to place the same color on every pixel that you pass over.) Now move the pencil tool cursor to the color palette to select a color. Once the pencil tool appears over a particular color, clicking once on the left mouse button places that color in the pencil tool.

    You can paint the icon using any colors you want. Figure 24-2 is a 32 by 32 pixel KT icon in the colors blue, black, and yellow. (B is black, + is blue, and Y is yellow.)

    Image from book
    Figure 24-2: Layout for 32 × 32 KT icon

  3. When you have colored all the pixels in the KT icon, Save All in the project and return to the main window (Form1) template. Now you must make the connection between the icon (Icon1.ico) and the PC operating system (so the icon can be used as a shortcut to the project on the PC desktop).

  4. Move to the Solution Explorer window at the top right of the IDE. Double-click on Properties (under Project Icon) to bring up the following window in the center of the IDE:

    Image from book
    Figure 24-3: Property pages

  5. When this window opens, the (Default Icon) setting appears in the Resources area (at the center of the window). There will be no picture of an icon to the left of the setting. Click on the down arrow at the right edge of the Icon list box. Click on the Browse icon at the far right edge of the Icon list box to open an Add existing file to project window. You must use this window to navigate to the icon file in this project (at C:\Visual Studio2005\Projects\DemosSourceCode\ProjectIcon).

    The list of files in the Add existing file to project window will look like Figure 24-4.

    Image from book
    Figure 24-4: Add existing file to project window

  6. Double-click on Icon1.ico to place the icon into the project. Immediately a small picture of the icon will appear to the left of the Icon text box. The icon is now a permanent part of this project. If someone chooses to create a shortcut icon to this project later, this is the icon that will appear.

Version 2.0 of the Visual Studio C# compiler embeds this icon into the executable (ProjectIcon.exe in this case), so the icon file, Icon1.ico, need not be transported along with the ProjectIcon.exe executable to other PCs. Version 1.0 required that the programmer keep the icon file in the same directory as the executable, but the version 2.0 architects removed this impediment.


Manually Placing the Icon on the Desktop

  1. Compile and run this project in the IDE (to make sure that all is well), Save All, then exit the IDE.

  2. Move to My Documents in the PC, then to Visual Studio 2005\Projects\DemosSourceCode\ProjectIcon\bin\debug, and highlight the ProjectIcon.exe file.

  3. Right-click on this file and click on Create Shortcut. The operating system will create a shortcut to ProjectIcon.exe in the same directory as the executable. Drag and drop this shortcut entry onto the desktop. The icon that is displayed is the one you created earlier in the Icon Editor.

Note

In the next chapter, we discuss how to create an icon on one’s desktop automatically, using C# code.


Chapter 25: Creating an Install Program

Overview

Once a group of computer programmers has finished creating a series of executable programs and grouped them under the umbrella of a top-level entry program (as in Chapter 23), it is time to write the code that will transfer those programs to a customer’s PC hard drive as a “suite” of programs that is invoked by the click of an icon (shortcut) on the desktop. The purpose of this chapter is to discuss how this “deployment” program is created and some of the issues confronted at that time.

Most installation programs transfer three kinds of files from the delivery vehicle (usually a CD) to the customer’s hard drive: executables, data files necessary for internal bookkeeping, and read-only files (such as helps). In this demonstration, a deployment suite named KT101 is developed to transfer executable and bookkeeping files to the C:\Program Files\KT101 subdirectory. There are no help files included. An icon (shortcut) is placed on the PC desktop to invoke the KT101 suite, which consists of seven executables and five bookkeeping files (*.dta). The shortcut placed on the desktop opens one of the seven executables, KT101EntryPoint, and KT101EntryPoint opens the other six executables in turn.

If this demonstration becomes too tedious, there is a completed deployment program installed from the companion files in C:\Documents and Settings\Owner\My Documents\Visual Studio 2005\Projects\ KT101\KT101DeploymentDemoSpare. The completed deployment program’s executable, KT101DeploymentDemoSpare.msi, is located at KT101DeploymentDemoSpare\KT101DeploymentDemoSpare\Debug.

With the advent of the Setup and Deployment specialty project capability built into the C# v2.0 compiler, there is no need any longer to use one’s manual deployment schemes, such as was used with the C++ language. There is one big difference with the automated C# Setup and Deployment project and the old manual deployment programs that we designed in the past: The automated C# project places all the files and folders that are placed in the deployment project into the PC’s registry, while the manual deployment schemes never used the registry. Therefore, the manual deployment schemes were difficult to remove at a later date.

During the construct of the modern, automatic Setup and Deployment project, the programmer is required to drag and drop every file and folder that is to be deployed by the new project. This is not hard to do. The automatic project does not physically move each file and folder to itself; it stores a “path” to each element instead (in a small database). The file/folder is copied to the deployment program only when the programmer clicks on the Build | Build Solution menu item (the last step in the process).

Since the automatic project brings all the files and folders to itself and copies them into the output file (*.msi) at project build time, this output file is huge (several megs in size is not unusual).

The automatic project requires that the programmer name the entry point into the group of programs as a primary output of the project itself. There again, this is not difficult to do, but the programmer must keep her thinking cap on at all times. This is tedious work.


Project KT101DeploymentDemo

When you have completed the creation of project KT101DeploymentDemo, it will do the following:

  • Create directory C:\Program Files\KT101 and subdirectory C:\Program Files\KT101\DDT.

  • Transfer five read-write files from the deployment program to subdirectory C:\Program Files\KT101. These files are necessary to perform internal Keeping Track bookkeeping. The files are named Masterfile.dta, KT101Buy_Sold.dta, KT101WeeklyNetWorth.dta, CalendarYear.dta, and CashInPortfolio.dta. The first three files are empty, the fourth file contains four characters (2005), and the fifth file contains nine characters (000000.00).

  • Copy the KT101EntryPoint.exe executable into the PC subdirectory C:\Program Files\KT101.

  • Copy icon Icon1.ico to the desktop. This shortcut will be pathed to the executable KT101EntryPoint.

  • Copy six more executables to C:\Program Files\KT101 that are called by the K101TEntryPoint entry-point program: KT101Startup, KT101Buy_Sold, KT101Plotter, KT101Realphabetize, KT101WeeklyDataPosting, and KT10CopyToFloppy.

To aid in the development of this deployment project, copies of the shortcut icon, the six more executables named above, and five bookkeeping files (*.dta) have all been gathered together in one location in the C:\Documents and Settings\Owner\My Documents\Visual Studio 2005\Projects\KT101\Files KT101 Deploy Chap25 subdirectory. This is done for convenience only; the programmer could just as well have dragged and dropped the files from 12 separate locations on his PC.

Creating Bookkeeping Files for This Demo

The five bookkeeping files — Masterfile.dta, KT101Buy_Sold.dta, KT101WeeklyNetWorth.dta, CalendarYear.dta, and CashInPortfolio.dta — were all created in WordPad. The first three files are empty, and the last two have one record of data in each. They are all read-write files, and are located in the Files KT101 Deploy Chap25 subdirectory.

Creating Project KT101DeploymentDemo

  1. Open the Visual Studio IDE, and click on File | New. The New Project window shown in Figure 25-1 appears.

    Image from book
    Figure 25-1: New Project window

  2. In the Project types list, highlight Windows under Visual C#. In the same list, highlight Setup and Deployment below Other Project Types.

  3. In the Templates area, highlight Setup Project. Then change the name of the project from “Setup1” to KT101DeploymentDemo.In the Location box, set the location to C:\Documents and Settings\ Owner\My Documents\Visual Studio 2005\Projects\KT101. Make sure that the “Create directory for solution” check box is checked, then click on the OK button.

    The project window should look like Figure 25-2.

    Image from book
    Figure 25-2: KT101DeploymentDemo window

  4. Before proceeding any further, open the file My Documents\ Visual Studio 2005\Projects\KT101\Files KT101 Deploy Chap25, and display it on the main window at the top left of the screen. This subdirectory contains 12 files, all of which will eventually be transferred to this deployment project.

    Image from book
    Figure 25-3: Files KT101 Deploy Chap25

Rules for Movement within a Deployment Development Screen

There are subtle rules for moving about the deployment development screen:

  • Whenever the words “KT101DeploymentDemo” are highlighted in the Solution Explorer (the far-right column on the IDE screen), seven small icons will appear at the top of the Solution Explorer. The six to the left are editors; the seventh is the property pages window.

    The six editors are named File System Editor (the default editor), Registry Editor (not used), File Types Editor (not used), User Interface Editor (briefly used), Custom Actions Editor (briefly used), and Launch Conditions Editor (not used). The seventh icon, the one to the farthest right, displays the property pages; it is not used in this demonstration.

  • When right-clicked, the top entry in the Solution Explorer, “Solution ‘KT101DeploymentDemo’ (1 project),” produces a menu for adding new projects to this solution set. We will add one more project (the program that is the entry point into KT101) to this deployment project.

  • When right-clicked, the second-line entry in the Solution Explorer, “KT101DeploymentDemo,” produces a menu for adding items (files, folders, etc.) to the existing deployment project. However, we will do all the file and folder adding by using drag and drop techniques, which are easier to do. We will eventually add 12 files to the existing deployment project.

Everything that you add to this deployment project (other folders or single files) exists here in virtual form only — they are merely copies of files that exist elsewhere. Copies of the necessary files are placed in the executable file only when the programmer begins the build process.

Setting the Application Folder Name

The Application Folder is the folder that will eventually be placed in the receiving PC’s Program Files directory by the deployment that is being created in this chapter. The creator of the application suite builds the deployment and sends the output of the deployment, a file named KT101DeploymentDemo.msi in this case, to the users who want to install the application suite on their PCs. When the deployment creator (programmer) has done everything right in the building of the deployment file, he will choose Build | Build Solution from the main menu to create the deployment executable. Next, all attention shifts to that executable, which will be located in KT101DeploymentDemo\ KT101DeploymentDemo\debug. If the msi executable places all the files in their proper places on the PC and registers the application with the PC’s registry, then it’s time to celebrate!

Place the File System Editor in the center of the screen if it is not there. This editor can be retrieved by clicking on KT101DeploymentDemo in the Solution Explorer (this action sets the icons for the editors at the top of the Solution Explorer). Click on the second icon from the left, File System Editor, to bring the editor into the center of the screen.

With the File System Editor showing in the center of the IDE (the editor has two columns of data — File System on Target Machine on the left and Name on the right), proceed as follows:

  1. Highlight Application Folder in the left column to place its properties in the leftmost column of the IDE (the Properties column). Change the Always Create property from False to True by highlighting Always Create in the left column of the Properties window, clicking on the down arrow that appears in the right column of the Properties window, and picking True.

  2. Highlight the Default Location property to produce this long name in the right column:

[ProgramFilesFolder][Manufacturer]\[ProductName]

    The names Program Files Folder, Manufacturer, and Product Name are all taken from another page in this deployment scheme. To find these names, make sure that the KT101DeploymentDemo entry in the Solution Explorer is highlighted. Then a list of the project’s properties will appear in the Properties listing. The formal name for “Program Files Folder” is always “Program Files” — it is never changed. You can choose any name you want for Manufacturer and Product Name. But the easy way to set the default location on the PC where this project will end up is to click on the entry, erase all of it, and type in C:\Program Files\KT101.

  3. During the installation program execution (performed by the user at his PC), the user may be offered the opportunity to change this location in the Program Files.

IMPORTANT

If you do not want to allow the user to change the location of the project in Program Files, then do the following:

With KT101DeploymentDemo highlighted in the Solution Explorer, click on the User Interface Editor icon at the top of the Solution Explorer window (which replaces the File System Editor in the center of the IDE). Delete the Installation Folder entry at this time (the first time it appears, after Install, Start, and Welcome). To do this, right-click on the Installation Folder entry and click on Delete. Now, the user will never be given the opportunity to change the location of the files/folders being deployed.

Since we do not want the user to change this path, perform the above and delete the Installation Folder entry in Install.

Adding a Subdirectory to C:\Program Files\KT101

We need to add a subdirectory named DDT to the top-level directory (the Application folder). Follow these steps:

  1. Make sure that the File System Editor is in the center of the IDE, and highlight Application Folder in the left column.

  2. Right-click on Application Folder to produce a context menu and click on Add, then Folder. A subfolder will appear below the Application Folder entry. Change this subfolder name to DDT. This subdirectory will be used by the suite of programs after deployment.

  3. Right-click on DDT to produce another context menu. Click on Properties Window to place its properties in the far-left properties list, and change the Always Create property from False to True. If you fail to do this, the DDT subdirectory will not be created during deployment.

Adding an Additional Project to This Deployment Solution Set

To add an additional project — in this case, the entry point project into KT101 — follow these steps:

  1. Move to the Solution Explorer on the right side of the IDE main window and right-click on the Solution ‘KT101DeploymentDemo’ (1 Project) entry in the Solution Explorer. Choose Add from the context menu, then pick Existing Project. The Add Existing Project window appears, as shown in Figure 25-4. (You must navigate to C:\Documents and Settings\Owner\My Documents\Visual Studio 2005\Projects\KT101 to view this list in the Add Existing Project window.)

    Image from book
    Figure 25-4: Add Existing Project window

  2. Highlight KT101EntryPoint, and then click on the Open button at the lower right of the window to display the lower-tier list of files in the project. Double-click on the KT101EntryPoint folder in this new list to reveal three lower-tier folders and the KT101EntryPoint.csproj file. Double-click on the KT101EntryPoint. csproj file to transfer this project to the deployment project. When this project has been added to the deployment project, the right side of the main window in the IDE will look like Figure 25-5.

    Image from book
    Figure 25-5: All projects aboard

IMPORTANT

This is the one case where drag and drop won’t work (to add this new project to the original KT101DeploymentDemo project).

In case you are wondering why we went to such heroic lengths to transfer the complete KT101EntryPoint project to this deployment project but ignored the other six executable projects in the KT101 suite, this is the reason: As a result of this deployment, a shortcut icon must be created to point to this project, KT101EntryPoint, to bring the suite into the PC initially. The pointing process requires that the complete KT101EntryPoint project be placed inside the deployment project. The other six executables will be copied to the installation folder as simple executables.

Note the new project that has been added, KT101EntryPoint, goes to the top of the Solution Explorer, pushing down the original project, KT101DeploymentDemo. The top line in the Solution Explorer now shows two projects in this suite.

Adding 12 Files to This Deployment Project

The 12 files listed in Files KT101 Deploy Chap25 will be added to the deployment project (not to the solution set).

Make sure that the File System Editor occupies the center of the screen. It is time to drag and drop the 12 files/folders. When Application Folder is highlighted in the leftmost column of the File System Editor, the only item that appears in the rightmost column is the DDT entry, a subdirectory that you added earlier.

Drag and drop each of the 12 items from the Files KT101 … listing into the rightmost column of the File System Editor, under DDT.

The only entry that has not been entered in the KT101DeploymentDemo is the Primary output from KT101EntryPoint. We will do that now.

Converting an Executable to Project Output Form

Project KT101EntryPoint has been included in this deployment project as the second project and must be executed automatically every time the user clicks on the KT101 shortcut on the desktop. KT101EntryPoint must be placed into the deployment project in the Project Output form so this can happen.

  1. Right-click on the KT101DeploymentDemo entry near the top of the Solution Explorer list. Click on Add, and then on Project Output. The Add Project Output Group window appears.

    Image from book
    Figure 25-6: Add Project Output Group window

  2. If you had not transferred the KT101EntryPoint project to this deployment project earlier, the Project entry list at the top of the Add Project Output Group window would be empty. To create the KT101EntryPoint Project Output, double-click on the Primary output entry (the first entry) in the list of six possible choices in this window. This action places the entry Primary Output from KT101EntryPoint into the list of elements in Solution Explorer. After this is accomplished, the right side of the deployment project main window in the IDE should look like Figure 25-7.

    Image from book
    Figure 25-7: All outputs aboard

Creating a Shortcut Icon and Pathing It to KT101EntryPoint

It takes a special action within the deployment project to prepare KT101EntryPoint for execution when the desktop shortcut icon is clicked by the user.

  1. Highlight KT101DeploymentDemo on the Solution Explorer (to bring up the six editor icons at the top of the Solution Explorer), and click on the Custom Actions Editor to bring the editor into the middle of the screen.

  2. Right-click on the Custom Actions entry (just above the word Install) in the editor. A context menu named Add Custom Action appears. Click on Add Custom Action to open the Select Item in Project window.

  3. Double-click on the Application Folder entry in the new window list to bring it to the top of the window.

    Image from book
    Figure 25-8: Select Item in Project window

  4. Double-click on the Primary output from KT101EntryPoint entry (which may be mostly hidden at the right edge of the list). The main window will look like the following figure:

    Image from book
    Figure 25-9: Special action

    At this point we have created the primary output of KT101EntryPoint, but we have not created the path between the desktop and this output. Now it is time to set the shortcut icon.

  5. Return to the File System Editor and highlight the entry User’s Desktop in the left column of the editor. Right-click on the blank column to the right of the User’s Desktop column. If you click on the left column, this next context menu will not appear. This seems like a foolish instruction (right-click on the blank column to the right), but it is the only way to create the icon. In the context menu that appears, click on Create New Shortcut to open the Select Item in Project window.

  6. Double-click on Application Folder, then double-click on the Icon1.ico file in the list of items in the Application Folder. The Select Item in Project window disappears, and in the rightmost column of the File System Editor (in the center of the IDE) the words “Shortcut to Icon1.ico” appear. This is the default name given to the shortcut in this deployment. Highlight the name and change it to KT101.

  7. Right-click on KT101 and choose Properties Window from the context menu to open the icon’s Properties window. Click on the Icon property to display the down arrow in the right column, and click on the down arrow. Click on Browse to find an icon. The Icon window appears.

    Image from book
    Figure 25-10: Icon window

  8. Click on the Browse button near the top of the window. The Select Item in Project window reappears. Double-click on Application Folder; the Icon1.ico file that you placed there earlier should be shown in the list. Highlight Icon1.ico and click on the OK button at the bottom of the window. A second Icon window appears, as shown in Figure 25-11.

    Image from book
    Figure 25-11: Icon window

  9. Double-click on the yellow KT icon to set it into the deployment project. The window closes.

The Final Connection

Everything has been determined in the icon area except what the “target” will be when the user clicks on the desktop icon.

  1. To set this, return to the File System Editor and click on User’s Desktop. This causes the shortcut name KT101 to appear in the right column.

  2. Highlight KT101 to bring its properties into the Properties window. Scroll down to the Target property, highlight it, and click on the three dots in the right column (Browse). The Select Item in Project window opens to display all items within the Application Folder.

  3. Highlight Primary output from KT101EntryPoint (Active) (which is located at the far right edge of the window). Then click on the OK button to make the connection between the desktop icon and the executable KT101EntryPoint in the Application Folder.

Now that this desktop shortcut is set up, it will initiate the executable KT101EntryPoint every time it is clicked.

Compiling the Deployment Project

At the main IDE menu, pick Build | Build Solution. Later, at the bottom of the screen, the message “Build Successful” will appear. There is no need to run the output of the deployment project at this time. If you want to look at the KT101DeploymentDemo.msi executable (the deployment program output), you will find it at My Documents\Visual Studio 2005\KT101\KT101DeploymentDemo\Debug. The *.msi output is about 445 K in size.

Save All and exit the C# compiler.

Executing the Deployment Project

Warning

This is a full-fledged installation program and is subject to the same restrictions as any other installation program you may receive with commercial software. You cannot run this program if it has been run earlier on this PC unless you remove previous installations. So, before you attempt to execute this msi program, you must go to the Control Panel, choose Add or Remove Programs, find the KT101DeploymentDemo project, and remove it. The worst thing you can do is navigate to Program Files and KT101, then simply delete the KT101 folder. If you do this, the remove mechanism in Control Panel will never work!

If all parts of KT101 are removed from your PC, then navigate to My Documents\Visual Studio 2005\Projects\KT101\KT101DeploymentDemo\Debug and execute KT101DeploymentDemo.msi. Good luck! If all goes well, the C:\Program Files\KT101DeploymentDemo subdirectory should look like Figure 25-12.

Image from book
Figure 25-12: KT101 files listing

To send a copy of the KT101 suite to a friend, place the msi file KT101DeploymentDemo on a CD. On this same CD add the Microsoft file dotnetfx.exe (22.4 meg in size). This is a self-extracting file that must be placed on the friend’s PC — this is the giant cookie that C# uses to execute its programs on a PC. The dotnetfx.exe file can be downloaded from a link at www.wordware.com/unlockingcsharp. All Windows XP operating system sales after mid-2005 should have this cookie included in the package.

This completes the description of a deployment project. This project writes to the PC registry and installs all files necessary for the complete solution set. Therefore, the only way to remove this solution set is by using the formal methods in Control Panel | Add or Remove Programs.

Note

There is a third financial suite in the companion files named TISourceCode that contains exactly the same executables as KT (Keeping Track). This suite is provided for users who want to track financial data with two programs on one PC. TI is exactly the same as KT, but the letters “KT” in the original suite are replaced with “TI.” (In case you’re wondering, “TI”’ stands for “TIAA-CREF,” and is used by the author to track his retirement funds in TIAA-CREF.)


Chapter 26: Methods (Subroutines)

There has always been a need for special methods (earlier called subroutines) that support calculations, repetitive processes, and more in major programs. In this chapter, to demonstrate a subroutine build, we create the simplest subroutine ever known: adding two integers together.

This is the main (and only) window:

Image from book
Figure 26-1: Methods window

Project Methods

This project, Methods, calls a subroutine named Subroutine() to add two integers together. The complete project is located at Visual Studio 2005\Projects\DemosSourceCode\Methods.

Three integers (IntegerA, IntegerB, and IntegerC) are declared in line SR013. The subroutine Subroutine() is defined in lines SR016-SR020. This subroutine returns an integer to the calling program, so it must be declared as public int. If a subroutine returns nothing, it is declared as type public void.

The user must first enter an integer in each of the text boxes (there is no type-checking here). In an actual program it would be important to check that each entry is in fact a number and an integer. As soon as the user clicks on the Do the Addition button, the button1_Click event handler swings into action (lines SR030-SR036).

The subroutine is called in line SR034. The answer it returns is placed in IntegerC. The answer is converted to a string and is placed into label5 at line SR035. Label5 appears on the main window between the Do the Addition button and the Quit button.

The source code for Methods is given below.

Methods (Subroutine) (SR) Listing

Form1.cs:
SR002:        using System;
SR003:        using System.Collections.Generic;
SR004:        using System.ComponentModel;
SR005:        using System.Data;
SR006:        using System.Drawing;
SR007:        using System.Windows.Forms;

SR009:        namespace Methods
SR010:        {
SR010:          partial class Form1 : Form
SR012:          {
SR013:            public int IntegerA, IntegerB, IntegerC;

SR014:            public Form1()
SR015:            {InitializeComponent(); }

                  // Here is the global 'Method':
SR016:            public static int Subroutine(int intOne, int intTwo)
SR017:            {
SR018:              int Number = intOne + intTwo;
SR019:              return(Number);
SR020:            }
//-----------------------------------------------------------------------------------------//
SR030:            private void button1_Click(object sender, EventArgs e)
SR031:            { // Do the addition.
SR032:              IntegerA = Convert.ToInt32(textBox1.Text);
SR033:              IntegerB = Convert.ToInt32(textBox2.Text);

                    // Here method 'Subroutine' is used:
SR034:              IntegerC = Subroutine(IntegerA, IntegerB);
SR035:              label5.Text = "The answer is: " + IntegerC.ToString()+".";
SR036:            }
//-----------------------------------------------------------------------------------------//
SR040:             private void button2_Click(object sender, EventArgs e)
SR041:             { // Quit.
SR042:               Close();
SR043:             }
SR044:          }
SR045:        }
//========================================================================================//
Form1.Designer.cs:
SR100:        namespace Methods
SR101:        {
SR102:          partial class Form1
SR103:          {
                  // Required designer variable.
SR104:            private System.ComponentModel.IContainer components = null;
                  // Clean up any resources being used.
SR105:            protected override void Dispose(bool disposing)
SR106:            {
SR107:              if (disposing && (components != null)) components.Dispose();
SR108:              base.Dispose(disposing);
SR109:            }

SR110:            #region Windows Form Designer generated code
                  // Required method for Designer support.
SR111:            private void InitializeComponent()
SR112:            {
SR113:              this.label1 = new System.Windows.Forms.Label();
SR114:              this.label2 = new System.Windows.Forms.Label();
SR115:              this.label3 = new System.Windows.Forms.Label();
SR116:              this.label4 = new System.Windows.Forms.Label();
SR117:              this.textBox1 = new System.Windows.Forms.TextBox();
SR118:              this.textBox2 = new System.Windows.Forms.TextBox();
SR119:              this.label5 = new System.Windows.Forms.Label();
SR120:              this.button1 = new System.Windows.Forms.Button();
SR121:              this.button2 = new System.Windows.Forms.Button();
SR122:              this.SuspendLayout();
                    //
                    // label1
                    //
SR123:              this.label1.AutoSize = true;
SR124:              this.label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 14F,
                      System.Drawing.FontStyle.Underline, System.Drawing.GraphicsUnit.Point,
                      ((byte)(0)));
SR125:              this.label1.Location = new System.Drawing.Point(145, 16);
SR126:              this.label1.Name = "label1";
SR127:              this.label1.Size = new System.Drawing.Size(201, 24);
SR128:              this.label1.TabIndex = 0;
SR129:              this.label1.Text = "Methods (Subroutines)";
                    //
                    // label2
                    //
SR130:              this.label2.AutoSize = true;
SR131:              this.label2.Font = new System.Drawing.Font("Microsoft Sans Serif", 10F,
                      System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point,
                      ((byte)(0)));
SR132:              this.label2.Location = new System.Drawing.Point(23, 64);
SR133:              this.label2.Name = "label2";
SR134:              this.label2.Size = new System.Drawing.Size(431, 17);
SR135:              this.label2.TabIndex = 1;
SR136:              this.label2.Text = "This program adds two integers together and writes
                      the answer below.";
                    //
                    // label3
                    //
SR137:              this.label3.AutoSize = true;
SR138:              this.label3.Font = new System.Drawing.Font("Microsoft Sans Serif", 10F,
                      System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point,
                      ((byte)(0)));
SR139:              this.label3.Location = new System.Drawing.Point(53, 89);
SR140:              this.label3.Name = "label3";
SR141:              this.label3.Size = new System.Drawing.Size(127, 17);
SR142:              this.label3.TabIndex = 2;
SR143:              this.label3.Text = "Enter \'Integer A\': -->";
                    //
                    // label4
                    //
SR144:              this.label4.AutoSize = true;
SR145:              this.label4.Font = new System.Drawing.Font("Microsoft Sans Serif", 10F,
                      System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point,
                      ((byte)(0)));
SR146:              this.label4.Location = new System.Drawing.Point(232, 89);
SR147:              this.label4.Name = "label4";
SR148:              this.label4.Size = new System.Drawing.Size(126, 17);
SR149:              this.label4.TabIndex = 3;
SR150:              this.label4.Text = "Enter \'Integer \'B\' -->";
                    //
                    // textBox1
                    //
SR151:              this.textBox1.Location = new System.Drawing.Point(176, 88);
SR152:              this.textBox1.Name = "textBox1";
SR153:              this.textBox1.Size = new System.Drawing.Size(40, 20);
SR154:              this.textBox1.TabIndex = 4;
                    //
                    // textBox2
                    //
SR155:              this.textBox2.Location = new System.Drawing.Point(358, 88);
SR156:              this.textBox2.Name = "textBox2";
SR157:              this.textBox2.Size = new System.Drawing.Size(40, 20);
SR158:              this.textBox2.TabIndex = 5;
                    //
                    // label5
                    //
SR159:              this.label5.AutoSize = true;
SR160:              this.label5.Font = new System.Drawing.Font("Microsoft Sans Serif", 10F,
                      System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point,
                      ((byte)(0)));
SR161:              this.label5.Location = new System.Drawing.Point(160, 168);
SR162:              this.label5.Name = "label5";
SR163:              this.label5.Size = new System.Drawing.Size(20, 17);
SR164:              this.label5.TabIndex = 6;
SR165:              this.label5.Text = "L5";
                    //
                    // button1
                    //
SR166:              this.button1.Location = new System.Drawing.Point(187, 130);
SR167:              this.button1.Name = "button1";
SR168:              this.button1.Size = new System.Drawing.Size(125, 25);
SR169:              this.button1.TabIndex = 7;
SR170:              this.button1.Text = "Do the Addition";
SR171:              this.button1.Click += new System.EventHandler(this.button1_Click);
                    //
                    // button2
                    //
SR172:              this.button2.Location = new System.Drawing.Point(212, 200);
SR173:              this.button2.Name = "button2";
SR174:              this.button2.Size = new System.Drawing.Size(75, 25);
SR175:              this.button2.TabIndex = 8;
SR176:              this.button2.Text = "Quit";
SR177:              this.button2.Click += new System.EventHandler(this.button2_Click);
                    //
                    // Form1
                    //
SR178:              this.AutoScaleDimensions = new System.Drawing.SizeF(5F, 13F);
SR179:              this.ClientSize = new System.Drawing.Size(484, 248);
SR180:              this.Controls.Add(this.button2);
SR181:              this.Controls.Add(this.button1);
SR182:              this.Controls.Add(this.label5);
SR183:              this.Controls.Add(this.textBox2);
SR184:              this.Controls.Add(this.textBox1);
SR185:              this.Controls.Add(this.label4);
SR186:              this.Controls.Add(this.label3);
SR187:              this.Controls.Add(this.label2);
SR188:              this.Controls.Add(this.label1);
SR189:              this.Name = "Form1";
SR190:              this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
SR191:              this.Text = "Methods (Subroutines)";
SR192:              this.ResumeLayout(false);
SR193:              this.PerformLayout();
SR194:            }
SR195:            #endregion

SR196:            private System.Windows.Forms.Label label1;
SR197:            private System.Windows.Forms.Label label2;
SR198:            private System.Windows.Forms.Label label3;
SR199:            private System.Windows.Forms.Label label4;
SR200:            private System.Windows.Forms.TextBox textBox1;
SR201:            private System.Windows.Forms.TextBox textBox2;
SR202:            private System.Windows.Forms.Label label5;
SR203:            private System.Windows.Forms.Button button1;
SR204:            private System.Windows.Forms.Button button2;
SR205:          }
SR206:        }


Chapter 27: Game-Making and Animation

Overview

Game-making is a lot of fun, and in the process of creating games, most programmers hone their logic coding skills. This chapter discusses three projects that are located in Visual Studio 2005\Projects\ DemosSourceCode. The first two involve game-making:

  • TicTacToe is an ageless game played by two players. This project includes full scorekeeping since most tic-tac-toe devotees play a series of games at one setting.

  • BattingPractice builds a minimal baseball playing field and allows the “batter” to swing at pitches thrown by the “pitcher.” Scorekeeping is not included in this project, but we suggest some ways that a programmer can enhance this project.

The third project introduces animation:

  • The RollingBlock project “rolls” a block along a flat surface at variable speeds, and the user can increase or decrease the rolling rate at the click of a button.

The last discussion in this chapter is about why there are no games that use bitmaps.


Project TicTacToe

Image from book
Figure 27-1: TicTacToe window

A complete listing of the Form1.cs source code is shown at the end of this section. For a complete listing of Form1.Designer.cs, see App D --DemoVC#TaggedList.doc in Visual Studio 2005\Tagged KT-Demo Code.

Game Board Layout

The main part of the game board consists of nine panels that overlap by one pixel at their edges.

  1. Create the large 100 x 100 panels first so they can be numbered panel1 through panel9. The panel is one of the controls shown on the IDE Toolbox when the entire form is highlighted.

  2. Place a black edge on each of the panels by highlighting that panel (to place its properties in the Properties window), clicking on Appearance | Border Style, and choosing Fixed Single.

  3. While you are there, you make this panel transparent so window painting will appear on the screen (not behind the panels). If the panel is not transparent, then any painting hides what is underneath the panel. Click on Back Color to reveal a down arrow at the right of the line. Click on the down arrow to reveal a list of colors with tabs across the top of the new window. Click on the Web tab, then select Transparent. By making the panel transparent, anything that is painted onto the window will appear at the forefront to the user.

    These nine large panels have no event handlers associated with them. However, at the middle of each large panel, a tiny 10 x 10 panel is placed to act as the control element for each large panel.

  4. Place the second group of nine 10 x 10 panels numbered panel10 through panel18 to act as the control element for each large panel. These panels force the user to click carefully on the portion of the game board where he wants to place the next X or O.

  5. Create an event handler for each of the nine tiny panels by highlighting the panel, clicking on the Events icon to bring up the event handlers, then double-clicking on Mouse | Mouse Down to place the panelxx_MouseDown event handler in the source code.

Warning

If you simply double-click on one of the small panels, you will create the default handler for a panel, which is a paint handler. You don’t want this! To delete an unwanted default handler from a panel, highlight the tiny panel (to puts its properties in the Properties window), click on the Events icon, and click on the Appearance | Paint item that is already highlighted. Right-click on this item to bring up a pop-up (context) menu with two entries: Reset and Description. Click on Reset to remove the unwanted event handler from the source code.

Each of these nine 10 x 10 panels now has a mouse down event handler affixed to it, and when a player clicks on one of the panels, something happens! If a player is the X player, a large red X appears in the large panel area. If a player is the O player, a large blue O appears in the large panel area.

As soon as a player clicks on a 10 x 10 panel to place his marker there, the small panel is immediately disabled so the panel cannot be used again.

Painting an X or O on the Game Board

Each of the nine panels performs the same sequence of events when a player clicks on a 10 x 10 panel in the middle of a large panel. Here is the code for the top-left panel, which is numbered as 0:

TT0180:        private void panel10_MouseDown(object sender,
                                              System.Windows.Forms.MouseEventArgs e)
TT0181:        { // Top-left panel, position '0'.
TT0182:          panel10.Enabled = false;
TT0183:          if (XTurn)
TT0184:          {
TT0185:            SX[0] = 1;
TT0186:            XTurn = false;
TT0187:            label1.Text = "'O' Plays";
TT0188:          }
TT0189:          else
TT0190:          {
TT0191:            SO[0] = 1;
TT0192:            XTurn = true;
TT0193:            label1.Text = "'X' Plays";
TT0194:          }
TT0195:          WinnerW();
TT0196:          Invalidate();
TT0197:        }

Line TT0182 disables the panel so it cannot be selected again.

XTurn is the master counter. It is set to true when it is player X’s turn and false when it is player O’s turn.

Integer arrays SO[] and SX[] are all set to 0 when the game starts. If player X picks this panel, then SX[0] is set to 1 (the SX[0] position is the top-left panel). The integer 1 in this array means that it belongs either to the X player or the O player. As soon as the SO[] or SX[] array is posted, XTurn is flipped from true to false, or vice versa (lines TT0186 or TT0192). Line TT0195 immediately calls the method WinnerW() to check for a possible winner (and end the game).

Line TT0196 calls for a repainting of the screen with the command Invalidate().

Winner or No Winner?

As soon as the WinnerW() method is called, control moves to lines TT0030-TT0108. Note that this method is listed immediately after line TT0026, InitializeComponent(). This is the only place the programmer can place a global subroutine that services all other procedures in the form. The method is “void” because it returns nothing when it is called. All of its outputs are placed in public variables listed at the top of the public class Form1.

The WinnerW() method performs a myriad of tasks. The nine positions where a player can place an X or O are designated as positions 0 through 8:

  • Position 0: Top-left box

  • Position 1: Top-center box

  • Position 2: Top-right box

  • Position 3: Center-left box

  • Position 4: Center box

  • Position 5: Center-right box

  • Position 6: Bottom-left box

  • Position 7: Bottom-center box

  • Position 8: Bottom-right box

Therefore, the winning combinations of boxes that a player must fill to win are:

  • 0 plus 1 plus 2

  • 0 plus 4 plus 8

  • 0 plus 3 plus 6

  • 1 plus 4 plus 7

  • 2 plus 4 plus 6

  • 2 plus 5 plus 8

  • 3 plus 4 plus 5

  • 6 plus 7 plus 8

Neither player can possibly win until a total of five X’s and O’s have been placed on the screen, so line TT0035 doesn’t bother to check for a winner until that time. Lines TT0036-TT0084 perform all the checks to declare a winner, and when this occurs, either XWinner or OWinner is set to true. For scorekeeping purposes, the appropriate intXWins or intOWins number is incremented. If all nine plays have been made and there is no winner, line TT0080 detects the tie and increments the variable intTies in TT0083. Lines TT0087-TT0089 post the scores on the scoreboard.

Lines TT0093-TT0097 ask the players if they want to play the game again. If the answer is “no,” the button at the top of the screen is disabled in line TT0104. If the answer is “yes,” the button remains enabled, ready for the next game.

To recycle to play the next game, it is necessary to clear the red X’s and blue O’s from the screen. This is accomplished by setting bool Start to false, which stops the entering of X’s and O’s onto the screen (line TT0100).

Painting the X’s and O’s on the Screen

All screen painting occurs in lines TT0370-TT0394. Line TT0373 sets the font at 92-point Arial (1 inch equals 72 points). Since only one of the two drawn characters (X or O) can appear over any one panel (or no character appears there), the Form1_Paint() method uses a series of if…else statements like this:

TT0376:        if (SX[0] == 1) grfx.DrawString("X", font1, Brushes.Red, 39, 34);
TT0377:        else if (SO[0] == 1) grfx.DrawString("O", font1, Brushes.Blue, 34, 34);

The starting coordinates for each painted character were set to make an X or O appear in the middle of the panel. Note that the X coordinates are (39, 34) and the O coordinates are (34, 34).

Recycling for the Next Game

The players choose to play another game in line TT0097 in the WinnerW() method. The screen is cleared of any X or O characters since the boolean variable Start in line TT0374 has been set to false. As soon as the Start Game button is picked again, the button1_Click() method in lines TT0132-TT0157 is set into motion again.

Lines TT0132-TT0133 reset integer arrays SO[] and SX[] to all 0’s so the WinnerW() method will work correctly in the game being started. intNoOfClicks is reset to 0 in line TT0134.

TT0134:        intNoOfClicks = 0;

Lines TT0135-TT0143 reset the nine 10 x 10 panels to an enabled mode (each one was disabled in the earlier game when a player selected that panel to place her X or O in that position).

Setting Start = true in line TT0144 enables screen painting in line TT0374.

Line TT0145 increments intGameNumber so lines TT0146-TT0155 can prepare for the next game (deciding if X plays first or O plays first).

Line TT0156 prepares the screen for the next game.

TicTacToe (TT) Listing

Form1.cs:
TT0001:       #region Using directives
TT0002:       using System;
TT0003:       using System.Collections.Generic;
TT0004:       using System.ComponentModel;
TT0005:       using System.Data;
TT0006:       using System.Drawing;
TT0007:       using System.Windows.Forms;
TT0008:       #endregion

TT0009:       namespace TicTacToe
TT0010:       {
TT0011:         partial class Form1 : Form
TT0012:         {
                  // Programmer added variables.
TT0013:           public bool Start = false;
TT0014:           public bool XTurn = true;
TT0015:           public bool OWinner = false;
TT0016:           public bool XWinner = false;
TT0017:           public int intXWins = 0;
TT0018:           public int intOWins = 0;
TT0019:           public int intTies = 0;
TT0020:           public int intNumberOfGames = 0;
TT0021:           public int intNoOfClicks = 0;
TT0022:           public int intGameNumber = 0;
TT0023:           public int[] SO = new int[9];
TT0024:           public int[] SX = new int[9];

TT0025:           public Form1()
TT0026:           {InitializeComponent(); }
//-----------------------------------------------------------------------------------------//
TT0030:           public void WinnerW()
TT0031:           { // Has a person won the game?
TT0032:             intNoOfClicks++;
TT0033:             OWinner = false;
TT0034:             XWinner = false;
TT0035:             if (intNoOfClicks > 4)
TT0036:             { // Check for an 'O' Winner:
TT0037:               if ((SO[0] == 1) && (SO[1] == 1) && (SO[2] == 1))
TT0038:               { OWinner = true; goto JUMP; }

TT0039:               else if ((SO[0] == 1) && (SO[4] == 1) && (SO[8] == 1))
TT0040:               { OWinner = true; goto JUMP; }

TT0041:               else if ((SO[0] == 1) && (SO[3] == 1) && (SO[6] == 1))
TT0042:               { OWinner = true; goto JUMP; }

TT0043:               else if ((SO[1] == 1) && (SO[4] == 1) && (SO[7] == 1))
TT0044:               { OWinner = true; goto JUMP; }

TT0045:               else if ((SO[2] == 1) && (SO[4] == 1) && (SO[6] == 1))
TT0046:               { OWinner = true; goto JUMP; }

TT0047:               else if ((SO[2] == 1) && (SO[5] == 1) && (SO[8] == 1))
TT0048:               { OWinner = true; goto JUMP; }

TT0049:               else if ((SO[3] == 1) && (SO[4] == 1) && (SO[5] == 1))
TT0050:               { OWinner = true; goto JUMP; }

TT0051:               else if ((SO[6] == 1) && (SO[7] == 1) && (SO[8] == 1))
TT0052:               { OWinner = true; goto JUMP; }

                      // Check for an 'X' winner:
TT0053:               if ((SX[0] == 1) && (SX[1] == 1) && (SX[2] == 1))
TT0054:               { XWinner = true; goto JUMP; }

TT0055:               else if ((SX[0] == 1) && (SX[4] == 1) && (SX[8] == 1))
TT0056:               { XWinner = true; goto JUMP; }

TT0057:               else if ((SX[0] == 1) && (SX[3] == 1) && (SX[6] == 1))
TT0058:               { XWinner = true; goto JUMP; }

TT0059:               else if ((SX[1] == 1) && (SX[4] == 1) && (SX[7] == 1))
TT0060:               { XWinner = true; goto JUMP; }

TT0061:               else if ((SX[2] == 1) && (SX[4] == 1) && (SX[6] == 1))
TT0062:               { XWinner = true; goto JUMP; }

TT0063:               else if ((SX[2] == 1) && (SX[5] == 1) && (SX[8] == 1))
TT0064:               { XWinner = true; goto JUMP; }

TT0065:               else if ((SX[3] == 1) && (SX[4] == 1) && (SX[5] == 1))
TT0066:               { XWinner = true; goto JUMP; }

TT0067:               else if ((SX[6] == 1) && (SX[7] == 1) && (SX[8] == 1))
TT0068:               { XWinner = true; goto JUMP; }

TT0069:               JUMP:
                      // Has a winner been found?
TT0070:               if (OWinner)
TT0071:               {
TT0072:                 label8.Text = "'O' Wins! Game Over.";
TT0073:                 intOWins++;
TT0074:               }
TT0075:               else if (XWinner)
TT0076:               {
TT0077:                 label8.Text = "'X' Wins! Game Over.";
TT0078:                 intXWins++;
TT0079:               }
TT0080:               else if (intNoOfClicks == 9)
TT0081:               {
TT0082:                 label8.Text = "Tie Game. No Winner.";
TT0083:                 intTies++;
TT0084:               }

                      // Play another game?
TT0085:               if ((OWinner) || (XWinner) || (intNoOfClicks == 9))
TT0086:               {
                        // Post the scoreboard.
TT0087:                 label5.Text = intXWins.ToString();
TT0088:                 label6.Text = intOWins.ToString();
TT0089:                 label7.Text = intTies.ToString();

TT0090:                 intNumberOfGames++;
                        // Post number of games played to main window.
TT0091:                 label10.Text = intNumberOfGames.ToString();
TT0092:                 Invalidate();

TT0093:                 string strMessage;
TT0094:                 if (OWinner) strMessage = "'O' Wins! Play Another Game?";
TT0095:                 else if (XWinner) strMessage = "'X' Wins! Play Another Game?";
TT0096:                 else strMessage = " Tie Game. Play Another Game?";

TT0097:                 DialogResult dr = MessageBox.Show(strMessage, "Question:",
                        MessageBoxButtons.YesNo, MessageBoxIcon.Question);

TT0098:                 if (dr == DialogResult.Yes)
TT0099:                 {
TT0100:                   Start = false; // Turn off the 'X's and 'O's.
TT0101:                   Invalidate();
TT0102:                 }
TT0103:                 else
TT0104:                 button1.Enabled = false;
TT0105:               }
TT0106:               Invalidate();
TT0107:             }
TT0108:           }
//-----------------------------------------------------------------------------------------//
TT0120:           private void Form1_Load(object sender, EventArgs e)
TT0121:           { // Game Initialization.
TT0122:             label5.Text = intXWins.ToString();
TT0123:             label6.Text = intOWins.ToString();
TT0124:             label7.Text = intTies.ToString();
TT0125:           }
//-----------------------------------------------------------------------------------------//
TT0130:           private void button1_Click(object sender, EventArgs e)
TT0131:           { // Start game.
TT0132:             for (int jj = 0; jj < 9; jj++) SO[jj] = 0;
TT0133:             for (int kk = 0; kk < 9; kk++) SX[kk] = 0;
TT0134:             intNoOfClicks = 0;
TT0135:             panel10.Enabled = true;
TT0136:             panel11.Enabled = true;
TT0137:             panel12.Enabled = true;
TT0138:             panel13.Enabled = true;
TT0139:             panel14.Enabled = true;
TT0140:             panel15.Enabled = true;
TT0141:             panel16.Enabled = true;
TT0142:             panel17.Enabled = true;
TT0143:             panel18.Enabled = true;

TT0144:             Start = true;

TT0145:             intGameNumber++;
                    // If this next statement is 'true' then this next game will be
                    // an 'ODD' numbered game. If 'ODD', then 'X' leads.
TT0146:             if ((intNumberOfGames / 2) == ((intNumberOfGames + 1) / 2))
TT0147:             {
TT0148:               label1.Text = "'X' Plays"; // ODD game coming up.
TT0149:               XTurn = true;
TT0150:             }
TT0151:             else
TT0152:             {
TT0153:               label1.Text = "'O' Plays"; // EVEN game coming up.
TT0154:               XTurn = false;
TT0155:             }
TT0156:             Invalidate();
TT0157:           }
//-----------------------------------------------------------------------------------------//
TT0170:           private void button2_Click(object sender, EventArgs e)
TT0171:           { // Quit.
TT0172:             Close();
TT0173:           }
//-----------------------------------------------------------------------------------------//
TT0180:           private void panel10_MouseDown(object sender,
                    System.Windows.Forms.MouseEventArgs e)
TT0181:           { // Top-left panel, position '0'.
TT0182:             panel10.Enabled = false;
TT0183:             if (XTurn)
TT0184:             {
TT0185:               SX[0] = 1;
TT0186:               XTurn = false;
TT0187:               label1.Text = "'O' Plays";
TT0188:             }
TT0189:             else
TT0190:             {
TT0191:               SO[0] = 1;
TT0192:               XTurn = true;
TT0193:               label1.Text = "'X' Plays";
TT0194:             }
TT0195:             WinnerW();
TT0196:             Invalidate();
TT0197:           }
//-----------------------------------------------------------------------------------------//
TT0200:           private void panel11_MouseDown(object sender,
                    System.Windows.Forms.MouseEventArgs e)
TT0201:           { // Top-center panel, position '1'.
TT0202:             panel11.Enabled = false;
TT0203:             if (XTurn)
TT0204:             {
TT0205:               SX[1] = 1;
TT0206:               XTurn = false;
TT0207:               label1.Text = "'O' Plays";
TT0208:             }
TT0209:             else
TT0210:             {
TT0211:               SO[1] = 1;
TT0212:               XTurn = true;
TT0213:               label1.Text = "'X' Plays";
TT0214:             }
TT0215:             WinnerW();
TT0216:             Invalidate();
TT0217:           }
//-----------------------------------------------------------------------------------------//
TT0220:           private void panel12_MouseDown(object sender,
                    System.Windows.Forms.MouseEventArgs e)
TT0221:           { // Top-right panel, position '2'.
TT0222:             panel12.Enabled = false;
TT0223:             if (XTurn)
TT0224:             {
TT0225:               SX[2] = 1;
TT0226:               XTurn = false;
TT0227:               label1.Text = "'O' Plays";
TT0228:             }
TT0229:             else
TT0230:             {
TT0231:               SO[2] = 1;
TT0232:               XTurn = true;
TT0233:               label1.Text = "'X' Plays";
TT0234:             }
TT0235:             WinnerW();
TT0236:             Invalidate();
TT0237:           }
//-----------------------------------------------------------------------------------------//
TT0240:           private void panel13_MouseDown(object sender,
                    System.Windows.Forms.MouseEventArgs e)
TT0241:           { // Center-left panel, position '3'.
TT0242:             panel13.Enabled = false;
TT0243:             if (XTurn)
TT0244:             {
TT0245:               SX[3] = 1;
TT0246:               XTurn = false;
TT0247:               label1.Text = "'O' Plays";
TT0248:             }
TT0249:             else
TT0250:             {
TT0251:               SO[3] = 1;
TT0252:               XTurn = true;
TT0253:               label1.Text = "'X' Plays";
TT0254:             }
TT0255:             WinnerW();
TT0256:             Invalidate();
TT0257:           }
//-----------------------------------------------------------------------------------------//
TT0260:           private void panel14_MouseDown(object sender,
                    System.Windows.Forms.MouseEventArgs e)
TT0261:           { // Center-center panel, position '4'.
TT0262:             panel14.Enabled = false;
TT0263:             if (XTurn)
TT0264:             {
TT0265:               SX[4] = 1;
TT0266:               XTurn = false;
TT0267:               label1.Text = "'O' Plays";
TT0268:             }
TT0269:             else
TT0270:             {
TT0271:               SO[4] = 1;
TT0272:               XTurn = true;
TT0273:               label1.Text = "'X' Plays";
TT0274:             }
TT0275:             WinnerW();
TT0276:             Invalidate();
TT0277:           }
//-----------------------------------------------------------------------------------------//
TT0280:           private void panel15_MouseDown(object sender,
                    System.Windows.Forms.MouseEventArgs e)
TT0281:           { // Center-right panel, position '5'.
TT0282:             panel15.Enabled = false;
TT0283:             if (XTurn)
TT0284:             {
TT0285:               SX[5] = 1;
TT0286:               XTurn = false;
TT0287:               label1.Text = "'O' Plays";
TT0288:             }
TT0289:             else
TT0290:             {
TT0291:               SO[5] = 1;
TT0292:               XTurn = true;
TT0293:               label1.Text = "'X' Plays";
TT0294:             }
TT0295:             WinnerW();
TT0296:             Invalidate();
TT0297:           }
//-----------------------------------------------------------------------------------------//
TT0300:           private void panel16_MouseDown(object sender,
                    System.Windows.Forms.MouseEventArgs e)
TT0301:           { // Bottom-left panel, position '6'.
TT0302:             panel16.Enabled = false;
TT0303:             if (XTurn)
TT0304:             {
TT0305:               SX[6] = 1;
TT0306:               XTurn = false;
TT0307:               label1.Text = "'O' Plays";
TT0308:             }
TT0309:             else
TT0310:             {
TT0311:               SO[6] = 1;
TT0312:               XTurn = true;
TT0313:               label1.Text = "'X' Plays";
TT0314:             }
TT0315:             WinnerW();
TT0316:             Invalidate();
TT0317:           }
//-----------------------------------------------------------------------------------------//
TT0320:           private void panel17_MouseDown(object sender,
                    System.Windows.Forms.MouseEventArgs e)
TT0321:           { // Bottom-center panel, position '7'.
TT0322:             panel17.Enabled = false;
TT0323:             if (XTurn)
TT0324:             {
TT0325:               SX[7] = 1;
TT0326:               XTurn = false;
TT0327:               label1.Text = "'O' Plays";
TT0328:             }
TT0329:             else
TT0330:             {
TT0331:               SO[7] = 1;
TT0332:               XTurn = true;
TT0333:               label1.Text = "'X' Plays";
TT0334:             }
TT0335:             WinnerW();
TT0336:             Invalidate();
TT0337:           }
//-----------------------------------------------------------------------------------------//
TT0340:           private void panel18_MouseDown(object sender,
                    System.Windows.Forms.MouseEventArgs e)
TT0341:           { // Bottom-right panel, position '8'.
TT0342:             panel18.Enabled = false;
TT0343:             if (XTurn'
TT0344:             {
TT0345:               SX[8] = 1;
TT0346:               XTurn = false;
TT0347:               label1.Text = "'O' Plays";
TT0348:             }
TT0349:             else
TT0350:             {
TT0351:               SO[8] = 1;
TT0352:               XTurn = true;
TT0353:               label1.Text = "'X' Plays";
TT0354:             }
TT0355:             WinnerW();
TT0356:             Invalidate();
TT0367:           }
//-----------------------------------------------------------------------------------------//
TT0370:           private void Form1_Paint(object sender, PaintEventArgs e)
TT0371:           { // Paint the window.
TT0372:             Graphics grfx = e.Graphics;
TT0373:             Font font1 = new Font("Arial", 92);
TT0374:             if (Start)
TT0375:             {
TT0376:               if (SX[0] == 1) grfx.DrawString("X", font1, Brushes.Red, 39, 34);
TT0377:               else if (SO[0] == 1) grfx.DrawString("O", font1, Brushes.Blue, 34, 34);

TT0378:               if (SX[1] == 1) grfx.DrawString("X", font1, Brushes.Red, 138, 34);
TT0379:               else if (SO[1] == 1) grfx.DrawString("O", font1, Brushes.Blue, 133, 34);

TT0380:               if (SX[2] == 1) grfx.DrawString("X", font1, Brushes.Red, 237, 34);
TT0381:               else if (SO[2] == 1) grfx.DrawString("O", font1, Brushes.Blue, 232, 34);

TT0382:               if (SX[3] == 1) grfx.DrawString("X", font1, Brushes.Red, 39, 133);
TT0383:               else if (SO[3] == 1) grfx.DrawString("O", font1, Brushes.Blue, 34, 133);

TT0384:               if (SX[4] == 1) grfx.DrawString("X", font1, Brushes.Red, 138, 133);
TT0385:               else if (SO[4] == 1) grfx.DrawString("O", font1, Brushes.Blue, 133,
                               133);

TT0386:               if (SX[5] == 1) grfx.DrawString("X", font1, Brushes.Red, 237, 133);
TT0387:               else if (SO[5] == 1) grfx.DrawString("O", font1, Brushes.Blue, 232,
                               133);

TT0388:               if (SX[6] == 1) grfx.DrawString("X", font1, Brushes.Red, 39, 232);
TT0389:               else if (SO[6] == 1) grfx.DrawString("O", font1, Brushes.Blue, 34, 233);

TT0390:               if (SX[7] == 1) grfx.DrawString("X", font1, Brushes.Red, 138, 232);
TT0391:               else if (SO[7] == 1) grfx.DrawString("O", font1, Brushes.Blue, 133,
                               233);

TT0392:               if (SX[8] == 1) grfx.DrawString("X", font1, Brushes.Red, 237, 232);
TT0393:               else if (SO[8] == 1) grfx.DrawString("O", font1, Brushes.Blue, 232,
                               233);
TT0394:             }
TT0395:           }
TT0396:         }
TT0397:       }

Form1.Designer.cs:
[See 'TicTacToe' listing in 'App D -- DemoVC#TaggedList.doc'.]


Project BattingPractice

To view this project, open the IDE and open the BattingPractice project in Visual Studio 2005\Projects\DemosSourceCode. Compile and run the project. The main window looks like this:

Image from book
Figure 27-2: Batting Practice window

When the Start button is picked, this window opens:

Image from book
Figure 27-3: Batting Practice Field window

For ease of baseball diamond construction, we used panels for each of the bases and the pitcher’s mound. The blue stick in the lower-right corner of the window is the bat. The ball is a red letter o and appears only after the Pitch The Ball button is selected.

All the action in this project occurs on Form 2.

Pitching the Ball

Lines BP118-BP119 are 31 sets of X and Y coordinates that dictate the baseball’s trajectory. If you do not like this method of prescribing the trajectory, then start the baseball at coordinates (290, 260) and increment the trajectory by 8, 10, or 12 after each timer1_Tick (lines BP140-BP150).

The timer that controls the baseball is located in lines BP128-BP131. This timer runs continuously, and its timer-ticks are either used or unused in lines BP140-BP150, based on whether the boolean variable BallStart is true or false. Note that these same lines of code are shown in the button1_Click method (lines BP202-BP205), and they are commented out. If the timer in lines BP128-BP131 is used, then it is turned on once (line BP131) and never turned off between pitches.

If the timer in button1_Click is used, then the timer must be turned on for each pitch and turned off when the pitch is complete. The worst thing a programmer can do in this situation is to turn the timer on several times but forget to ever turn it off. This causes the timer to emit ticks in an erratic manner and spoils the game.

Click on the Pitch The Ball button and the baseball will proceed from the pitcher’s mound to the home plate. The speed of the pitch is controlled by line BP113, intBallTimer1Interval = 200;. To increase the baseball velocity, decrease the number 200. Later in this section we suggest that you place a button on the batting practice field to increase or decrease the baseball speed as batting practice progresses, but this means that the timer must be moved from BP128-BP131 to BP202-BP205. Moving the timer means that it must be turned on and off after each pitch. This second timer configuration is demonstrated in the Rolling Block project later in this chapter.

Swinging the Bat

Lines BP120-BP121 are 16 sets of cosines and sines that dictate the position of the bat swing. Each position is 10 degrees beyond its predecessor (10 positions for the first 90 degrees). The timer that controls the bat swing is located in lines BP132-BP135. This timer runs continuously, and its timer-ticks are either used or unused in lines BP160-BP177, based on whether the boolean variable BatStart is true or false. Note that these same lines of code are shown in the button2_ Click method (lines BP222-BP225), and they are commented out. If the timer in lines BP132-BP135 is used, then it is turned on once (line BP135) and never turned off.

If the timer in button2_Click is used, then the timer must be turned on for each pitch and turned off when the pitch is complete.

Click on the Swing! button and the bat will proceed from the starting position to a position 150 degrees later (a right-handed batter). The speed of the bat swing is controlled by line BP114, intBatTimer2Interval = 100;. To increase the bat velocity, decrease the number 100. Later in this section we suggest that you place a button on the batting practice field to increase or decrease the bat speed as the batting practice progresses. This means that the timer must be moved from BP132-BP135 to BP222-BP225.

Determining if It’s a Hit!

This is the real bell ringer in this project. All of this logic is located in method timer2_Tick(), and it is not very good. In lines BP169-BP173 we attempt to determine if the ball and the bat are located at the same place at the same time, and conclude that this is a “hit.”

              // Check for a 'hit'.
BP169:        if ((intBallLoc == 24) || (intBallLoc == 25) /*||(intBallLoc==26)*/)
BP170:        {
BP171:          if ((intBatLoc == 4) || (intBatLoc == 5) /*||(intBatLoc==6)*/ )
BP172:          {
BP173:            label1.Text = "It's a HIT !!!";
BP174:          }

These three ball locations, (24, 25, and 26) and three bat locations (4, 5, and 6) cover the area of the home plate, so this scheme should work (to determine if the ball is hit). Sometimes this works, and sometimes it doesn’t.

A Project in Progress

At this point you’ll probably realize that project BattingPractice is really a work in progress. There are many things you could add to this project to make it better:

  • Provide buttons to increase and decrease the ball speed on Form2. The variable you want to decrease or increase is intBallTimerInterval.

  • Provide buttons to increase and decrease the bat speed on Form2. The variable you want to decrease or increase is intBatTimer2Interval.

  • Provide a scoreboard on Form2 that will keep track of the number of pitches thrown and the number of hits. To initialize the scoreboard data, we suggest that you add an event handler named Form2_Load() to Form2. To add this event handler, highlight the Form2 template (to bring up the window properties in the Properties window), click on the Events icon, move to Behavior | Load, and double-click on Load. Within this event handler you must initialize the scoreboard data (see project TicTacToe, lines TT0120-TT0125).

  • If you name the datakeepers intNoOfPitches and intNoOfHits, then these variables must be declared near line BP125 and initialized in Form2_Load(), which does not exist at this time. Within these event handlers you must also initialize the scoreboard data.

  • Provide a more accurate means of determining if the bat swing causes a hit. Perhaps one way would be to increase the number of locations of the ball between the pitcher’s mound and home plate (from 31 locations to 62, for example). If you do this, you must decrease the intBallTimer1Interval from 200 to 100, since there are twice as many images of the baseball to be shown in the same length of time.

    Good luck!

    BattingPractice (BP) Listing

Form1.cs:
BP002:        using System;
BP003:        using System.Collections.Generic;
BP004:        using System.ComponentModel;
BP005:        using System.Data;
BP006:        using System.Drawing;
BP007:        using System.Windows.Forms;

BP009:        namespace BattingPractice
BP010:        {
BP011:          partial class Form1 : Form
BP012:          {
BP013:            public Form1()
BP014:            {InitializeComponent(); }
//-----------------------------------------------------------------------------------------//
BP020:            private void button1_Click(object sender, EventArgs e)
BP021:            { // Start.
BP022:              Form2 frm2 = new Form2();
BP023:              frm2.ShowDialog();
BP024:              frm2.Close();
BP025:            }
//-----------------------------------------------------------------------------------------//
BP030:            private void button2_Click(object sender, EventArgs e)
BP031:            { // Quit.
BP032:              Close();
BP033:            }
BP034:          }
BP035:        }
//=========================================================================================//
Form1.Designer.cs:
BP040:        namespace BattingPractice
BP041:        {
BP042:          partial class Form1
BP043:          {
                  // Required designer variable.
BP044:            private System.ComponentModel.IContainer components = null;
                  // Clean up any resources being used.
BP045:            protected override void Dispose(bool disposing)
BP046:            {
BP047:              if (disposing && (components != null)) components.Dispose();
BP048:              base.Dispose(disposing);
BP049:            }

BP050:            #region Windows Form Designer generated code
                  // Required method for Designer support.
BP051:            private void InitializeComponent()
BP052:            {
BP053:              this.label1 = new System.Windows.Forms.Label();
BP054:              this.button1 = new System.Windows.Forms.Button();
BP055:              this.button2 = new System.Windows.Forms.Button();
BP056:              this.SuspendLayout();
                    //
                    // label1
                    //
BP057:              this.label1.AutoSize = true;
BP058:              this.label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 14F,
                      System.Drawing.FontStyle.Underline, System.Drawing.GraphicsUnit.Point,
                      ((byte)(0)));
BP059:              this.label1.Location = new System.Drawing.Point(77, 8);
BP060:              this.label1.Name = "label1";
BP061:              this.label1.Size = new System.Drawing.Size(141, 24);
BP062:              this.label1.TabIndex = 0;
BP063:              this.label1.Text = "Batting Practice";
                    //
                    // button1
                    //
BP064:              this.button1.Font = new System.Drawing.Font("Microsoft Sans Serif", 10F,
                      System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point,
                      ((byte)(0)));
BP065:              this.button1.Location = new System.Drawing.Point(75, 48);
BP066:              this.button1.Name = "button1";
BP067:              this.button1.Size = new System.Drawing.Size(150, 25);
BP068:              this.button1.TabIndex = 1;
BP069:              this.button1.Text = "Start";
BP070:              this.button1.Click += new System.EventHandler(this.button1_Click);
                    //
                    // button2
                    //
BP071:              this.button2.Location = new System.Drawing.Point(8, 93);
BP072:              this.button2.Name = "button2";
BP073:              this.button2.Size = new System.Drawing.Size(75, 25);
BP074:              this.button2.TabIndex = 2;
BP075:              this.button2.Text = "Quit";
BP076:              this.button2.Click += new System.EventHandler(this.button2_Click);
                    //
                    // Form1
                    //
BP077:              this.AutoScaleDimensions = new System.Drawing.SizeF(5F, 13F);
BP078:              this.ClientSize = new System.Drawing.Size(292, 128);
BP079:              this.Controls.Add(this.button2);
BP080:              this.Controls.Add(this.button1);
BP081:              this.Controls.Add(this.label1);
BP082:              this.Name = "Form1";
BP083:              this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
BP084:              this.Text = "Batting Practice";
BP085:              this.ResumeLayout(false);
BP086:              this.PerformLayout();
BP087:            }
BP088:            #endregion

BP089:            private System.Windows.Forms.Label label1;
BP090:            private System.Windows.Forms.Button button1;
BP091:            private System.Windows.Forms.Button button2;
BP092:          }
BP093:        }
//=========================================================================================//
Form2.cs:
BP100:        #region Using directives
BP101:        using System;
BP102:        using System.Collections.Generic;
BP103:        using System.ComponentModel;
BP104:        using System.Data;
BP105:        using System.Drawing;
BP106:        using System.Text;
BP107:        using System.Windows.Forms;
BP108:        #endregion

BP109:        namespace BattingPractice
BP110:        {
BP111:          partial class Form2 : Form
BP112:          {
                  // Programmer-added variables.
                  // Set ball speed. The lower the number, the FASTER the ball moves.
BP113:            public int intBallTimer1Interval = 200;
                  // Set bat speed. The lower the number, the FASTER the bat moves.
BP114:            public int intBatTimer2Interval = 100;
                  // BBPX = BatPivotPointX.
BP115:            public int iBPPX = 525;
                  // BPPY = BatPivotPointY.
BP116:            public int iBPPY = 525;
BP117:            public double dBL = 40.0;

BP118:            public int[] BallX = new int[31]{290,300,310,320,330,340,350,360,370,
                    380,390,400,410,420,430,440,450,460,470,480,490,500,510,520,530,540,
                    550,560,570,580,590};
BP119:            public int[] BallY = new int[31]{260,270,280,290,300,310,320,330,340,
                    350,360,370,380,390,400,410,420,430,440,450,460,470,480,490,500,510,
                    520,530,540,550,560};
                  // -20,-10,0,10,20,30,40,50,60,70,80,90,100,110,120 degrees.
BP120:            public double[] BatXCos = new double[16] {0.93969,0.98481,1.0,0.98481,
                    0.93969,0.86603,0.76604,0.64279,0.5,0.34202,0.17365,0.0,-0.17365,
                    -0.34202,-.5,0.93969};
BP121:            public double[] BatYSin = new double[16] {-0.34202,-0.17365,0.0,0.17365,
                    0.34202,0.5,0.64279,0.76604,0.86603,0.93969,0.98481,1.0,0.98481,
                    0.93969,0.86603,-0.34202};
BP122:            public int intBallLoc;  // Positions '0' through '30'.
BP123:            public int intBatLoc;   // Positions '0' through 15'.
BP124:            public bool BallStart = false;
BP125:            public bool BatStart = false;

BP126:            public Form2()
BP127:            {
                    // 'Pitch the Ball' timer.
BP128:              Timer timer1 = new Timer();
BP129:              timer1.Tick += new EventHandler(timer1_Tick);
BP130:              timer1.Interval = intBallTimer1Interval;
BP131:              timer1.Start();

                    // 'Swing the Bat' timer.
BP132:              Timer timer2 = new Timer();
BP133:              timer2.Tick += new EventHandler(timer2_Tick);
BP134:              timer2.Interval = intBatTimer2Interval;
BP135:              timer2.Start();

BP136:              InitializeComponent();
BP137:            }
//-----------------------------------------------------------------------------------------//
BP140:            private void timer1_Tick(object sender, EventArgs e)
BP141:            { // 'Pitch The Ball' timer.
BP142:              if (BallStart)
BP143:              {
BP144:                 intBallLoc++;
BP145:                 if (intBallLoc <= 26)
BP146:                   Invalidate();
BP147:                 else
BP148:                   BallStart = false;
BP149:               }
BP150:             }
//-----------------------------------------------------------------------------------------//
BP160:             private void timer2_Tick(object sender, EventArgs e)
BP161:             { // 'Batter Swing' timer.
BP162:               if (BatStart)
BP163:               {
BP164:                 intBatLoc++;
BP165:                 if (intBatLoc <= 15)
BP166:                   Invalidate();
BP167:                 else
BP168:                   BatStart = false;

                       // Check for a 'hit'.
BP169:                 if ((intBallLoc == 24) || (intBallLoc == 25) /*||(intBallLoc==26)*/)
BP170:                 {
BP171:                   if ((intBatLoc == 4) || (intBatLoc == 5) /*||(intBatLoc==6)*/)
BP172:                   {
BP173:                     label1.Text = "It's a HIT !!!";
BP174:                   }
BP175:                 }
BP176:               }
BP177:             }
//-----------------------------------------------------------------------------------------//
BP180:             private void Form2_Paint(object sender, PaintEventArgs e)
BP181:             { // Paint the window.
BP182:               Graphics grfx = e.Graphics;
BP183:               Font ballfont = new Font("Arial", 8); // This is the baseball.
BP184:               Pen bluePen = new Pen(Color.Blue, 4); // This is the bat.
BP185:               if (BallStart)
BP186:               {
BP187:                 grfx.DrawString("O", ballfont, Brushes.Red,
BP188:                 BallX[intBallLoc], BallY[intBallLoc]);
BP189:               }

BP190:               if (BatStart) // This is the bat.
BP191:               {
BP192:                 grfx.DrawLine(bluePen, iBPPX, iBPPY, (iBPPX + (int)(dBL *
                         BatXCos[intBatLoc])), (iBPPY - (int)(dBL * BatYSin[intBatLoc])));
BP193:               }
BP194:               else
BP195:               {
                       // Show the blue bat.
                       grfx.DrawLine(bluePen, iBPPX, iBPPY, (iBPPX + (int)(dBL * BatXCos[0])),
                         (iBPPY - (int)(dBL * BatYSin[0])));
BP196:               }
BP197:             }
//-----------------------------------------------------------------------------------------//
BP200:             private void button1_Click(object sender, EventArgs e)
BP201:             {// Pitch the ball.
                     /* All of this has been moved to lines BP039 - 0142.
BP202:               Timer timer1 = new Timer();
BP203:               timer1.Tick += new EventHandler(timer1_Tick);
BP204:               timer1.Interval = 1000;
BP205:               timer1.Start(); */

BP206:               intBallLoc = 0;
BP207:               BallStart = true;
BP208:               label1.Text = "";
BP209:             }
//-----------------------------------------------------------------------------------------//
BP220:             private void button2_Click(object sender, EventArgs e)
BP221:             { // Batter Swing !
                     /* All of this has been moved from lines BP0132 - BP135.
BP222:               Timer timer2 = new Timer();
BP223:               timer2.Tick += new EventHandler(timer2_Tick);
BP224:               timer2.Interval = 1000;
BP225:               timer2.Start(); */

BP226:               intBatLoc = 0;
BP227:               BatStart = true;
BP228:             }
//-----------------------------------------------------------------------------------------//
BP230:             private void button3_Click(object sender, EventArgs e)
BP231:             { // Quit.
BP232:               Close();
BP233:             }
BP234:           }
BP235:        }
//=========================================================================================//
Form2.Designer.cs:
BP300:        namespace BattingPractice
BP301:        {
BP302:          partial class Form2
BP303:          {
                  // Required designer variable.
BP304:            private System.ComponentModel.IContainer components = null;
                  // Clean up any resources being used.
BP305:            protected override void Dispose(bool disposing)
BP306:            {
BP307:              if (disposing && (components != null)) components.Dispose();
BP308:              base.Dispose(disposing);
BP309:            }

BP310:            #region Windows Form Designer generated code
                  // Required method for Designer support.
BP311:            private void InitializeComponent()
BP312:            {
BP313:              this.components = new System.ComponentModel.Container();
BP314:              this.panel1 = new System.Windows.Forms.Panel();
BP315:              this.panel2 = new System.Windows.Forms.Panel();
BP316:              this.panel3 = new System.Windows.Forms.Panel();
BP317:              this.panel4 = new System.Windows.Forms.Panel();
BP318:              this.panel5 = new System.Windows.Forms.Panel();
BP319:              this.panel6 = new System.Windows.Forms.Panel();
BP320:              this.panel7 = new System.Windows.Forms.Panel();
BP321:              this.timer1 = new System.Windows.Forms.Timer(this.components);
BP322:              this.timer2 = new System.Windows.Forms.Timer(this.components);
BP323:              this.button1 = new System.Windows.Forms.Button();
BP324:              this.button2 = new System.Windows.Forms.Button();
BP325:              this.button3 = new System.Windows.Forms.Button();
BP326:              this.label1 = new System.Windows.Forms.Label();
BP327:              this.SuspendLayout();
                    //
                    // panel1
                    //
BP328:              this.panel1.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
BP329:              this.panel1.Location = new System.Drawing.Point(20, 520);
BP330:              this.panel1.Name = "panel1";
BP331:              this.panel1.Size = new System.Drawing.Size(531, 2);
BP332:              this.panel1.TabIndex = 0;
                    //
                    // panel2
                    //
BP333:              this.panel2.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
BP334:              this.panel2.Location = new System.Drawing.Point(550, 20);
BP335:              this.panel2.Name = "panel2";
BP336:              this.panel2.Size = new System.Drawing.Size(2, 501);
BP337:              this.panel2.TabIndex = 1;
                    //
                    // panel3
                    //
BP338:              this.panel3.BackColor = System.Drawing.Color.Transparent;
BP339:              this.panel3.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
BP340:              this.panel3.Location = new System.Drawing.Point(531, 501);
BP341:              this.panel3.Name = "panel3";
BP342:              this.panel3.Size = new System.Drawing.Size(20, 20);
BP343:              this.panel3.TabIndex = 2;
                    //
                    // panel4
                    //
BP344:              this.panel4.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
BP345:              this.panel4.Location = new System.Drawing.Point(531, 58);
BP346:              this.panel4.Name = "panel4";
BP347:              this.panel4.Size = new System.Drawing.Size(20, 20);
BP348:              this.panel4.TabIndex = 3;
                    //
                    // panel5
                    //
BP349:              this.panel5.BackColor = System.Drawing.Color.Transparent;
BP350:              this.panel5.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
BP351:              this.panel5.Location = new System.Drawing.Point(280, 250);
BP352:              this.panel5.Name = "panel5";
BP353:              this.panel5.Size = new System.Drawing.Size(20, 20);
BP354:              this.panel5.TabIndex = 4;
                    //
                    // panel6
                    //
BP355:              this.panel6.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
BP356:              this.panel6.Location = new System.Drawing.Point(88, 501);
BP357:              this.panel6.Name = "panel6";
BP358:              this.panel6.Size = new System.Drawing.Size(20, 20);
BP359:              this.panel6.TabIndex = 5;
                    //
                    // panel7
                    //
BP360:              this.panel7.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
BP361:              this.panel7.Location = new System.Drawing.Point(88, 58);
BP362:              this.panel7.Name = "panel7";
BP363:              this.panel7.Size = new System.Drawing.Size(20, 20);
BP364:              this.panel7.TabIndex = 6;
                    //
                    // timer1
                    //
BP365:              this.timer1.Tick += new System.EventHandler(this.timer1_Tick);
                    //
                    // timer2
                    //
BP366:              this.timer2.Tick += new System.EventHandler(this.timer2_Tick);
                    //
                    // button1
                    //
BP367:              this.button1.Location = new System.Drawing.Point(136, 534);
BP368:              this.button1.Name = "button1";
BP369:              this.button1.Size = new System.Drawing.Size(125, 25);
BP370:              this.button1.TabIndex = 7;
BP371:              this.button1.Text = "Pitch the Ball";
BP372:              this.button1.Click += new System.EventHandler(this.button1_Click);
                    //
                    // button2
                    //
BP373:              this.button2.Location = new System.Drawing.Point(352, 534);
BP374:              this.button2.Name = "button2";
BP375:              this.button2.Size = new System.Drawing.Size(125, 25);
BP376:              this.button2.TabIndex = 8;
BP377:              this.button2.Text = "Swing !";
BP378:              this.button2.Click += new System.EventHandler(this.button2_Click);
                    //
                    // button3
                    //
BP379:              this.button3.Location = new System.Drawing.Point(440, 12);
BP380:              this.button3.Name = "button3";
BP381:              this.button3.Size = new System.Drawing.Size(75, 25);
BP382:              this.button3.TabIndex = 9;
BP383:              this.button3.Text = "Quit";
BP384:              this.button3.Click += new System.EventHandler(this.button3_Click);
                    //
                    // label1
                    //
BP385:              this.label1.AutoSize = true;
BP386:              this.label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 36F,
                      System.Drawing.FontStyle.Underline, System.Drawing.GraphicsUnit.Point,
                      ((byte)(0)));
BP387:              this.label1.Location = new System.Drawing.Point(128, 16);
BP388:              this.label1.Name = "label1";
BP389:              this.label1.Size = new System.Drawing.Size(0, 0);
BP390:              this.label1.TabIndex = 10;
                    //
                    // Form2
                    //
BP391:              this.AutoScaleDimensions = new System.Drawing.SizeF(5F, 13F);
BP392:              this.ClientSize = new System.Drawing.Size(584, 562);
BP393:              this.Controls.Add(this.label1);
BP394:              this.Controls.Add(this.button3);
BP395:              this.Controls.Add(this.button2);
BP396:              this.Controls.Add(this.button1);
BP397:              this.Controls.Add(this.panel7);
BP398:              this.Controls.Add(this.panel6);
BP399:              this.Controls.Add(this.panel5);
BP400:              this.Controls.Add(this.panel4);
BP401:              this.Controls.Add(this.panel3);
BP402:              this.Controls.Add(this.panel2);
BP403:              this.Controls.Add(this.panel1);
BP404:              this.Name = "Form2";
BP405:              this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
BP406:              this.Text = "Batting Practice Field";
BP407:              this.Paint += new System.Windows.Forms.PaintEventHandler
                      (this.Form2_Paint);
BP408:              this.ResumeLayout(false);
BP409:              this.PerformLayout();
BP410:            }
BP411:            #endregion

BP412:            private System.Windows.Forms.Panel panel1;
BP413:            private System.Windows.Forms.Panel panel2;
BP414:            private System.Windows.Forms.Panel panel3;
BP415:            private System.Windows.Forms.Panel panel4;
BP416:            private System.Windows.Forms.Panel panel5;
BP417:            private System.Windows.Forms.Panel panel6;
BP418:            private System.Windows.Forms.Panel panel7;
BP419:            private System.Windows.Forms.Timer timer1;
BP420:            private System.Windows.Forms.Timer timer2;
BP421:            private System.Windows.Forms.Button button1;
BP422:            private System.Windows.Forms.Button button2;
BP423:            private System.Windows.Forms.Button button3;
BP424:            private System.Windows.Forms.Label label1;
BP425:          }
BP426:        }


Project RollingBlock

This final project demonstrates some of the rudiments of animation. Rolling Block consists of an orange-colored, square block that moves along a flat surface from right to left. The timer that controls the movement has been placed in the method that starts the block movement, so its intBlockTimer1Interval (lines RB072 and RB082) is resettable by the user. This is the main window:

Image from book
Figure 27-4: Rolling Block window

The block timer is initially set at 500, which means that the timer should tick every half-second. The complete source listing for Rolling Block is shown at the end of this section.

Pictorial Layout

The flat surface that the orange block “rolls” upon is a panel taken from the Toolbox. It is 450 pixels long and 2 pixels high. The Toolbox panel can be used to draw vertical lines or horizontal lines on a screen only. If the line is other than vertical or horizontal, it must be “painted” on.

Since the timer-tick rate must be changeable, the timer must be placed in the event handler of the Start the Block Rolling button, button1_Click(). Its initial intBlockTimer1Interval is declared in line RB013. This interval is modified in lines RB072 (button3_Click()) and RB082 (button4_Click()). Each time the interval is changed, the new number is posted in label2.Text at the top left of the window.

The positions of the block are dictated by the Angle array in line RB023. Each time the method timer1_Tick() commands the block to roll (lines RB090-RB109), the block rotates 15 degrees. After every six movements (0, 15, 30, 45, 60, 75 degrees) the start position for the block is moved to the left (along the flat surface) by a distance dBlockSize (line RB014).

Line RB015 declares an integer NumberOfSidesBFOTT (Number Of Sides Before Falling Off The Track). This number is set in line RB033 (in method Form1_Load()) so the block will not attempt to roll off the left edge of the window. Since this number is computed every time the program executes (in line RB033), the programmer has the option of increasing or decreasing the size of the block (line RB014).

All the painting action is controlled with the boolean variable RollTheBlock. If RollTheBlock is true, then lines RB126-RB143 are executed. If false, lines RB147-RB150 are executed. Lines RB147-RB150 create a static orange block on the right edge of the window.

The calculations to determine the position of ACorner, BCorner, CCorner, and DCorner (lines RB126-RB139) are based on sequential computation — that is, the ACorner is set, then the BCorner is computed based on the ACorner location. The ACorner starts off as the left corner on the flat surface. The BCorner is on the right of flat surface. The CCorner is at the top right of the block, and the DCorner is at the top left of the block.

Upgrades to RollingBlock

Since the rolling rate of the orange block is resettable, about the only item in the project that is fixed is the size of the block. You can create a button to make the block larger and a separate button to make the block smaller. Or, you can create one button named Change Block Size and ask the user (via a MessageBox control) whether the block should be made larger or smaller. The variable that must be changed is dBlockSize; it is declared originally in line RB014. This number can be changed in an event handler that is created to support a button.

RollingBlock (RB) Listing

Form1.cs:
RB002:        using System;
RB003:        using System.Collections.Generic;
RB004:        using System.ComponentModel;
RB005:        using System.Data;
RB006:        using System.Drawing;
RB007:        using System.Windows.Forms;

RB009:        namespace RollingBlock
RB010:        {
RB011:          partial class Form1 : Form
RB012:          {
                  // Programmer-added variables.
                  // Set the rolling rate. '1000' produces one tick each second.
                  // The smaller the number, the faster the rate of roll.
RB013:            public int intBlockTimer1Interval = 500;
                  // Block size.
RB014:            public double dBlockSize = 60; // Pixels.
                  // Number of block sides before falling off the track.
RB015:            public int iNumberOfSidesBFOTT;
RB016:            public int iBP = 0; // Block Position, '0' through '5'.
RB017:            public bool RollTheBlock = false;
RB018:            public double dStartXAtStartup = 417.0;
RB019:            public double dStartX;
RB020:            public double dStartY = 133.0;
RB021:            public int iHowManySidesRolled = 0;
RB022:            public double dConvert; // Pi/180.
RB023:            public double[] Angle = new double[6] { 0.0, 15.0, 30.0, 45.0, 60.0, 75.0 };
RB024:            public double ACornerX, ACornerY, BCornerX, BCornerY, CCornerX,
                    CCornerY, DCornerX, DCornerY;

RB025:            public Form1()
RB026:            {
RB027:              InitializeComponent();
                    // Do not place the TIMER here! You cannot change its 'BlockTimerInterval'
                    // easily if you place the timer here.
RB028:            }
//-----------------------------------------------------------------------------------------//
RB030:            private void Form1_Load(object sender, EventArgs e)
RB031:            { // Determine the coordinates of the four corners of the rolling block.
RB032:              dConvert = Math.PI / 180.0;
RB033:              iNumberOfSidesBFOTT = (450 - (int)dBlockSize) / (int)dBlockSize;
RB034:            }
//-----------------------------------------------------------------------------------------//
RB040:            private void button1_Click(object sender, EventArgs e)
RB041:            { // Start the block rolling.
                    // Timer.
RB042:              label2.Text = intBlockTimer1Interval.ToString();

RB043:              Timer timer1 = new Timer();
RB044:              timer1.Tick += new EventHandler(timer1_Tick);
RB045:              timer1.Interval = intBlockTimer1Interval;
RB046:              timer1.Start();

RB047:              dStartX = dStartXAtStartup;
RB048:              iBP = 0;
RB049:              RollTheBlock = true;
RB050:            }
//-----------------------------------------------------------------------------------------//
RB060:            private void button2_Click(object sender, EventArgs e)
RB061:            { // Quit.
RB062:              Close();
RB063:            }
//-----------------------------------------------------------------------------------------//
RB070:            private void button3_Click(object sender, EventArgs e)
RB071:            { // Increase rolling rate.
RB072:              intBlockTimer1Interval = intBlockTimer1Interval - 50;
RB073:              label2.Text = intBlockTimer1Interval.ToString();
RB074:            }
//-----------------------------------------------------------------------------------------//
RB080:            private void button4_Click(object sender, EventArgs e)
RB081:            { // Decrease rolling rate.
RB082:              intBlockTimer1Interval = intBlockTimer1Interval + 50;
RB083:              label2.Text = intBlockTimer1Interval.ToString();
RB084:            }
//-----------------------------------------------------------------------------------------//
RB090:            private void timer1_Tick(object sender, EventArgs e)
RB091:            { // Timer to roll the block.
RB092:              if (RollTheBlock)
RB093:              {
RB094:                iBP++;
RB095:                 if (iBP > 5)
RB096:                 {
RB097:                   iHowManySidesRolled++;
RB098:                   if (iHowManySidesRolled > iNumberOfSidesBFOTT - 1)
RB099:                   {
RB100:                     timer1.Stop();
RB101:                     iHowManySidesRolled = 0;
RB102:                     RollTheBlock = false;
RB103:                   }
RB104:                   iBP = 0;
RB105:                   dStartX = dStartX - dBlockSize;
RB106:                 }
RB107:                 Invalidate();
RB108:               }
RB109              }
//-----------------------------------------------------------------------------------------//
RB120:             private void Form1_Paint(object sender, PaintEventArgs e)
RB121:             {
RB122:               Graphics grfx = e.Graphics;
RB123:               Pen orangePen = new Pen(Color.Orange, 3); // Color of the 'block'.
RB124:               if (RollTheBlock)
RB125:               {
                       // Position the rolling block.
RB126:                 ACornerX = dStartX;
RB127:                 ACornerY = dStartY;
RB128:                 double dXB = Math.Cos(Angle[iBP] * dConvert);
RB129:                 double dYB = Math.Sin(Angle[iBP] * dConvert);
RB130:                 BCornerX = dStartX + dXB * dBlockSize;
RB131:                 BCornerY = dStartY - dYB * dBlockSize;
RB132:                 double dXC = dXB + Math.Cos((Angle[iBP] + 90.0) * dConvert);
RB133:                 double dYC = dYB + Math.Sin((Angle[iBP] + 90.0) * dConvert);
RB134:                 CCornerX = dStartX + dXC * dBlockSize;
RB135:                 CCornerY = dStartY - dYC * dBlockSize;
RB136:                 double dXD = Math.Cos((Angle[iBP] + 180.0) * dConvert);
RB137:                 double dYD = Math.Sin((Angle[iBP] + 180.0) * dConvert);
RB138:                 DCornerX = dStartX + (dXC + dXD) * dBlockSize;
RB139:                 DCornerY = dStartY - (dYC + dYD) * dBlockSize;

RB140:                 grfx.DrawLine(orangePen, (int)ACornerX, (int)ACornerY,
                         (int)BCornerX, (int)BCornerY);
RB141:                 grfx.DrawLine(orangePen, (int)BCornerX, (int)BCornerY,
                         (int)CCornerX, (int)CCornerY);
RB142:                 grfx.DrawLine(orangePen, (int)CCornerX, (int)CCornerY,
                         (int)DCornerX, (int)DCornerY);
RB143:                 grfx.DrawLine(orangePen, (int)DCornerX, (int)DCornerY,
                         (int)ACornerX, (int)ACornerY);
RB144:               }
RB145:               else // Print block at start-up.
RB146:             {
RB147:               grfx.DrawLine(orangePen, (int)dStartXAtStartup, (int)dStartY,
                       (int)(dStartXAtStartup + dBlockSize), (int)dStartY);
RB148:               grfx.DrawLine(orangePen, (int)(dStartXAtStartup + dBlockSize),
                       (int)dStartY, (int)(dStartXAtStartup + dBlockSize),
                       (int)(dStartY - dBlockSize));
RB149:               grfx.DrawLine(orangePen, (int)(dStartXAtStartup + dBlockSize),
                       (int)(dStartY - dBlockSize), (int)dStartXAtStartup,
                       (int)(dStartY - dBlockSize));
RB150:               grfx.DrawLine(orangePen, (int)dStartXAtStartup,
                       (int)(dStartY - dBlockSize), (int)dStartXAtStartup, (int)dStartY);
RB151:              }
RB152:            }
RB153:          }
RB154:        }
//========================================================================================//
Form1.Designer.cs:
RB200:        namespace RollingBlock
RB201:        {
RB202:          partial class Form1
RB203:          {
                  // Required designer variable.
RB204:            private System.ComponentModel.IContainer components = null;
                  // Clean up any resources being used.
RB205:            protected override void Dispose(bool disposing)
RB206:            {
RB207:              if (disposing && (components != null)) components.Dispose();
RB208:              base.Dispose(disposing);
RB209:            }

RB210:            #region Windows Form Designer generated code
                  // Required method for Designer support.
RB211:            private void InitializeComponent()
RB212:            {
RB213:              this.components = new System.ComponentModel.Container();
RB214:              this.button1 = new System.Windows.Forms.Button();
RB215:              this.button2 = new System.Windows.Forms.Button();
RB216:              this.panel1 = new System.Windows.Forms.Panel();
RB217:              this.timer1 = new System.Windows.Forms.Timer(this.components);
RB218:              this.button3 = new System.Windows.Forms.Button();
RB219:              this.button4 = new System.Windows.Forms.Button();
RB220:              this.label1 = new System.Windows.Forms.Label();
RB221:              this.label2 = new System.Windows.Forms.Label();
RB222:              this.SuspendLayout();
                    //
                    // button1
                    //
RB223:              this.button1.Location = new System.Drawing.Point(325, 16);
RB224:              this.button1.Name = "button1";
RB225:              this.button1.Size = new System.Drawing.Size(150, 25);
RB226:              this.button1.TabIndex = 0;
RB227:              this.button1.Text = "Start the Block Rolling";
RB228:              this.button1.Click += new System.EventHandler(this.button1_Click);
                    //
                    // button2
                    //
RB229:              this.button2.Location = new System.Drawing.Point(16, 160);
RB230:              this.button2.Name = "button2";
RB231:              this.button2.Size = new System.Drawing.Size(75, 25);
RB232:              this.button2.TabIndex = 1;
RB233:              this.button2.Text = "Quit";
RB234:              this.button2.Click += new System.EventHandler(this.button2_Click);
                    //
                    // panel1
                    //
RB235:              this.panel1.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
RB236:              this.panel1.Location = new System.Drawing.Point(25, 136);
RB237:              this.panel1.Name = "panel1";
RB238:              this.panel1.Size = new System.Drawing.Size(450, 2);
RB239:              this.panel1.TabIndex = 2;
                    //
                    // timer1
                    //
RB240:              this.timer1.Tick += new System.EventHandler(this.timer1_Tick);
                    //
                    // button3
                    //
RB241:              this.button3.Location = new System.Drawing.Point(152, 160);
RB242:              this.button3.Name = "button3";
RB243:              this.button3.Size = new System.Drawing.Size(150, 25);
RB244:              this.button3.TabIndex = 3;
RB245:              this.button3.Text = "Increase Rolling Rate";
RB246:              this.button3.Click += new System.EventHandler(this.button3_Click);
                    //
                    // button4
                    //
RB247:              this.button4.Location = new System.Drawing.Point(328, 160);
RB248:              this.button4.Name = "button4";
RB249:              this.button4.Size = new System.Drawing.Size(150, 25);
RB250:              this.button4.TabIndex = 4;
RB251:              this.button4.Text = "Decrease Rolling Rate";
RB252:              this.button4.Click += new System.EventHandler(this.button4_Click);
                    //
                    // label1
                    //
RB253:              this.label1.AutoSize = true;
RB254:              this.label1.Location = new System.Drawing.Point(32, 16);
RB255:              this.label1.Name = "label1";
RB256:              this.label1.Size = new System.Drawing.Size(114, 14);
RB257:              this.label1.TabIndex = 5;
RB258:              this.label1.Text = "Block Timer Interval =";
                    //
                    // label2
                    //
RB259:              this.label2.AutoSize = true;
RB260:              this.label2.Location = new System.Drawing.Point(140, 16);
RB261:              this.label2.Name = "label2";
RB262:              this.label2.Size = new System.Drawing.Size(23, 14);
RB263:              this.label2.TabIndex = 6;
RB264:              this.label2.Text = "500";
                    //
                    // Form1
                    //
RB265:              this.AutoScaleDimensions = new System.Drawing.SizeF(5F, 13F);
RB266:              this.ClientSize = new System.Drawing.Size(484, 200);
RB267:              this.Controls.Add(this.label2);
RB268:              this.Controls.Add(this.label1);
RB269:              this.Controls.Add(this.button4);
RB270:              this.Controls.Add(this.button3);
RB271:              this.Controls.Add(this.panel1);
RB272:              this.Controls.Add(this.button2);
RB273:              this.Controls.Add(this.button1);
RB274:              this.Name = "Form1";
RB275:              this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
RB276:              this.Text = "Rolling Block";
RB277:              this.Paint += new System.Windows.Forms.PaintEventHandler
                      (this.Form1_Paint);
RB278:              this.Load += new System.EventHandler(this.Form1_Load);
RB279:              this.ResumeLayout(false);
RB280:              this.PerformLayout();
RB281:            }
RB282:            #endregion

RB283:            private System.Windows.Forms.Button button1;
RB284:            private System.Windows.Forms.Button button2;
RB285:            private System.Windows.Forms.Panel panel1;
RB286:            private System.Windows.Forms.Timer timer1;
RB287:            private System.Windows.Forms.Button button3;
RB288:            private System.Windows.Forms.Button button4;
RB289:            private System.Windows.Forms.Label label1;
RB290:            private System.Windows.Forms.Label label2;
RB291:          }
RB292:        }


Why C# Games Don’t Use Bitmaps

If you examine the modern games being sold for PCs and Macs you will note that most of them use bitmaps as the primary visual presentation method. For example, if a person on the screen speaks, the only portion of the visual presentation that moves is the person’s mouth — not the complete body. With the software that the Visual Studio C# compiler provides, the programmer cannot insert a portion of the bitmap — the entire bitmap must be reworked and re-presented. Overwriting a basic bitmap with a small bitmap is tricky business for the graphics neophyte — but not for the professional graphics person who makes cartoons for everyday use on the media networks.

Future versions of the Visual Studio C# compiler will increase the graphics capability of the programs produced, but the ability to create professional-quality cartoons will remain the purview of highly sophisticated commercial software used at TV stations that rents for about $20,000 a month.


Chapter 28: Picture Boxes

Overview

The PictureBox is one of the controls in the Toolbox. When it is first placed on a template it looks just like the panel. It has no border initially, so the programmer must apply a border by highlighting the picture box (to place its properties in the Properties window), picking Appearance | Border Style, and choosing the Fixed Single option.

Below Border Style there is an Image entry area that allows you to place a *.bmp or *.png graphic into the project. (A .png is a Microsoft invention, and it works just like a .bmp.) If you click on Image, be prepared to browse for the name of a bitmap to place in the PictureBox control.

To view project Picture Box, enter the IDE and click on File on the main menu, followed by Open | Project/Solution. When the Open Project window opens, click on the Picture Box subdirectory in Visual Studio 2005\Projects\DemosSourceCode. As soon as the project enters the IDE, compile and run it. The main window looks like Figure 28-1. In this case the necessary *.png file, CogTrain2.PNG, was placed in the folder Visual Studio 2005\Projects\DemosSourceCode\Picture Box\bin\debug. This .png file is located in the same directory as PictureBox.exe.

Image from book
Figure 28-1: Picture Box window

If the graphic is too large to fit into the PictureBox control, it will simply overflow on all sides. There is no device in the Visual Studio C# compiler to make the picture fit exactly in the picture box.

What about the small panel at the lower center of the picture? What is that? This is a simple panel (not a picture box). We have no bitmap to place in this area, so it must remain blank. If we had created a second picture box (by dropping another picture box from the Toolbox onto the window template), then the second picture box would have shown under the first picture box — there is no way to force a second picture on top of the first picture. We did this to give you an idea of how hard it is to manipulate bitmap pictures on the screen.

If you had access to an image editor apart from the Visual Studio C# compiler, you could create a bitmap, place it onto the panel, then move the panel about on the screen. But this takes more tools than you possess — more software to buy, probably from a third-party vendor.

The Visual Studio C# compiler has statements that will index through a series of bitmaps to cause a picture to “animate,” but the sequence of bitmaps must be created elsewhere. Perhaps later versions of the Visual Studio C# compiler will include more tools to do this type of work.


Placing a Bitmap Picture on the Picture Box

To place the cog train picture into the picture box, follow these steps:

  1. Highlight the PictureBox control to bring its properties into the Properties window.

  2. Scroll down to the Image entry in the left column and click on the Browse button (the button on the right with three dots) to open the Select Resource window.

    Image from book
    Figure 28-2: Select Resource window

  3. Click on the Import button to display the Open window. Change the display to Visual Studio 2005\Projects\DemosSourceCode\Picture Box\bin\debug. This is where the CogTrain2.PNG graphic should be located.

    Image from book
    Figure 28-3: Open window

  4. Highlight the CogTrain2.PNG file and click on the Open button to bring this graphic into the Select Resource window. The graphic also appears in the pictureBox1 control on the main window.

This completes the Picture Box project.

Picture Box (PB) Listing

Form1.cs:
PB002:        using System;
PB003:        using System.Collections.Generic;
PB004:        using System.ComponentModel;
PB005:        using System.Data;
PB006:        using System.Drawing;
PB007:        using System.Windows.Forms;

PB009:        namespace Picture_Box
PB010:        {
PB011:          partial class Form1 : Form
PB012:          {
PB013:            public Form1()
PB014:            {InitializeComponent(); }
//-----------------------------------------------------------------------------------------//
PB020:            private void button1_Click(object sender, EventArgs e)
PB021:            { // Quit.
PB022:              Close();
PB023:            }
PB024:          }
PB025:        }
//========================================================================================//
Form1.Designer.cs:
PB100:        namespace Picture_Box
PB101:        {
PB102:          partial class Form1
PB103:          {
                  // Required designer variable.
PB104:            private System.ComponentModel.IContainer components = null;
                  // Clean up any resources being used.
PB105:            protected override void Dispose(bool disposing)
PB106:            {
PB107:              if (disposing && (components != null)) components.Dispose();
PB108:              base.Dispose(disposing);
PB109:            }

PB110:            #region Windows Form Designer generated code
                  // Required method for Designer support.
PB111:            private void InitializeComponent()
PB112:            {
PB113:              this.pictureBox1 = new System.Windows.Forms.PictureBox();
PB114:              this.label1 = new System.Windows.Forms.Label();
PB115:              this.button1 = new System.Windows.Forms.Button();
PB116:              this.panel1 = new System.Windows.Forms.Panel();
                    ((System.ComponentModel.ISupportInitialize)
                      (this.pictureBox1)).BeginInit();
PB117:              this.SuspendLayout();
                    //
                    // pictureBox1
                    //
PB118:              this.pictureBox1.BorderStyle = System.Windows.Forms.
                         BorderStyle.FixedSingle;
PB119:              this.pictureBox1.Image = Picture_Box.Properties.Resources.CogTrain2;
PB120:              this.pictureBox1.Location = new System.Drawing.Point(32, 56);
PB121:              this.pictureBox1.Name = "pictureBox1";
PB122:              this.pictureBox1.Size = new System.Drawing.Size(525, 250);
PB123:              this.pictureBox1.TabIndex = 0;
PB124:              this.pictureBox1.TabStop = false;
                    //
                    // label1
                    //
PB125:              this.label1.AutoSize = true;
PB126:              this.label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 14F,
                      System.Drawing.FontStyle.Underline, System.Drawing.GraphicsUnit.Point,
                      ((byte)(0)));
PB127:              this.label1.Location = new System.Drawing.Point(140, 16);
PB128:              this.label1.Name = "label1";
PB129:              this.label1.Size = new System.Drawing.Size(308, 24);
PB130:              this.label1.TabIndex = 1;
PB131:              this.label1.Text = "Cog Train at the Top of Pike\'s Peak";
                    //
                    // button1
                    //
PB132:              this.button1.Location = new System.Drawing.Point(16, 328);
PB133:              this.button1.Name = "button1";
PB134:              this.button1.Size = new System.Drawing.Size(75, 25);
PB135:              this.button1.TabIndex = 2;
PB136:              this.button1.Text = "Quit";
PB137:              this.button1.Click += new System.EventHandler(this.button1_Click);
                    //
                    // panel1
                    //
PB138:              this.panel1.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
PB139:              this.panel1.Location = new System.Drawing.Point(168, 295);
PB140:              this.panel1.Name = "panel1";
PB141:              this.panel1.Size = new System.Drawing.Size(30, 20);
PB142:              this.panel1.TabIndex = 3;
                    //
                    // Form1
                    //
PB143:              this.AutoScaleDimensions = new System.Drawing.SizeF(5F, 13F);
PB144:              this.ClientSize = new System.Drawing.Size(584, 367);
PB145:              this.Controls.Add(this.panel1);
PB146:              this.Controls.Add(this.button1);
PB147:              this.Controls.Add(this.label1);
PB148:              this.Controls.Add(this.pictureBox1);
PB149:              this.Name = "Form1";
PB150:              this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
PB151:              this.Text = "Picture Box";
                    ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit();
PB152:              this.ResumeLayout(false);
PB153:              this.PerformLayout();
PB154:            }
PB155:            #endregion

PB156:            private System.Windows.Forms.PictureBox pictureBox1;
PB157:            private System.Windows.Forms.Label label1;
PB158:            private System.Windows.Forms.Button button1;
PB159:            private System.Windows.Forms.Panel panel1;
PB160:          }
PB161:        }


Chapter 29: “Lost ” Controls/Graphics

Overview

The Microsoft IDE (integrated development environment) presents an efficient set of tools to create windows visually. Every now and then, however, something goes wrong within the file set and the programmer is forced into “damage control” mode. This chapter is a brief discussion on damage control.

Does losing controls sound silly? Suppose you want to move a label (by highlighting it to place its properties in the Properties window, moving to the bottom of the properties list, and resetting the label location) from (50, 100) to (100, 100). However, as you type the location you enter (1000, 100). You meant to move the label 50 pixels to the right, but you inadvertently moved it 950 pixels to the right. The label disappears from the screen template in the IDE. What should you do?

To aid in this discussion, open project LostControls in Visual Studio 2005\Projects\DemosSourceCode in the IDE. The main window looks like Figure 29-1.

Image from book
Figure 29-1: Lost Controls window

The panel has a fixed single border, which is chosen at the time the panel is created.


Case 1: Small Errors

The label at the top of the Lost Controls screen template is label1. Highlight this label, and when its properties appear in the Properties window, move to the bottom of the properties list and change its location from (190, 20) to (190, 70). What happens to the label? It moves under the panel.

Now what are you going to do? This is easy to fix. You know that the label exists and that every control is listed in the Properties window. When you click on the window template, the Form1 properties are displayed in the Properties window, as shown in Figure 29-2.

Image from book
Figure 29-2: Properties window

The line of text at the top that reads “Form1 System.Windows.Forms.Form” is a list box, and all controls in that list box may be listed by clicking on the down arrow to the right of the text. When you click on the down arrow, a list of controls is displayed: Form1, panel1, button1, label1, and label2. The missing label must be one of the labels in this list.

Click on label1, and immediately the position of label1 will be shown on the window template. Now you can move the label to its proper location by either dragging the rectangle with the mouse or by typing new X and Y coordinates in the label’s properties window.

Move label1 from location (190, 70) back to (190, 20).


Case 2: Large Errors

Highlight label1 (to bring its properties into the Properties window), scroll down to Location, and change (190, 20) to (1900, 20). The label disappears from the template. The only way you will find this label is to look carefully at its location and understand that the coordinate 1900 is far off at the right end of the PC screen. The error must be corrected by resetting the size in the Properties window since the label is off the screen and cannot be dragged with the mouse.


Case 3: Size Loss

Buttons, text boxes, etc., are prone to disappearing because their size suddenly decreases to (0, 0) or some other small number. Move to the window template, highlight the Quit button, and set its size in the Properties window to (0, 0). The button will disappear from the template until you highlight button1 in the top of the Properties window. After highlighting button1, a tiny square appears on the template at the current location of the button. This tiny square is the tip-off to the programmer that the size dimensions have collapsed. Move to the Properties window and reset the button size to (75, 25). This corrects the problem.


Creating Controls in a Specific Order

Since you verified earlier that label1 disappears underneath the panel directly below it when its location is changed from (190, 20) to (190, 70), how come we were able to construct a second label, “Label on Top of Panel 1,” that did not disappear below the panel? This label was constructed after the panel and placed within the panel, so it appears on top of the panel.

The only way to ensure that the controls appear in the proper position (one on top of the other) is to create them in an order such that the control that appears in the background is created first. You cannot, for example, create a radio button at some location, then place a group box on top of it, and then bring the radio button out to the front layer. If the group box was created after the radio button, it will hide the radio button, period.


Causing Text to Appear from Underneath a Control

In project TicTacToe in Chapter 27, we set the BackColor of each of the larger panels to Web | Transparent so the program could write a large X or O over each panel (in red or blue). This technique works for writing on a form, but it does not work for manipulating controls on a form to bring them to the front layer of a presentation.


Removing Installed Controls

Recall that you remove a control by highlighting the control, right-clicking it, and choosing Delete when the context menu appears. If you have created event handlers for controls that you are deleting, the text in the source code may or may not disappear. If the event handler has no code inside the curly brackets, then the event handler code will probably disappear. But if the programmer has typed anything into the event handler, it will remain when the control is deleted. As far as the project is concerned at this time, the event handler code has nothing to do with the project! If you are concerned that the handler code may still be included in the project, then comment the code out by placing /* at the start of the method and */ at the end. This way, if the project produces error statements the next time the project is compiled, you will know that the method is not removable. Simply remove the /* and */ to keep the compiler happy.

IMPORTANT

The biggest mistake-maker in the vicinity of the IDE window template is the mouse, and we know who controls the mouse. Since all controls can be dragged and dropped it is easy to mistakenly drop one control one top of another, and then all kinds of bad things happen. When this happens, the control underneath absorbs the control that is erroneously dropped on top of it (if the underneath control is larger in size than the control on top), and sorting out this mess may require the removal of some controls and replacement with new controls.

The listing for LostControls is given below.

LostControls (LC) Listing

Form1.cs:
LC002:        using System;
LC003:        using System.Collections.Generic;
LC004:        using System.ComponentModel;
LC005:        using System.Data;
LC006:        using System.Drawing;
LC007:        using System.Windows.Forms;

LC009:        namespace LostControls
LC010:        {
LC011:          partial class Form1 : Form
LC012:          {
LC013:            public Form1()
LC014:            {InitializeComponent(); }
//-----------------------------------------------------------------------------------------//
LC020:            private void button1_Click(object sender, EventArgs e)
LC021:            { // Quit.
LC022:              Close();
LC023:            }
LC024:          }
LC025:        }
//=========================================================================================//
Form1.Designer.cs:
LC050:        namespace LostControls
LC051:        {
LC052:          partial class Form1
LC053:          {
                  // Required designer variable.
LC054:            private System.ComponentModel.IContainer components = null;
                  // Clean up any resources being used.
LC055:            protected override void Dispose(bool disposing)
LC056:            {
LC057:              if (disposing && (components != null)) components.Dispose();
LC058:              base.Dispose(disposing);
LC059:            }

LC060:            #region Windows Form Designer generated code
                  // Required method for Designer support.
LC061:            private void InitializeComponent()
LC062:            {
LC063:              this.label1 = new System.Windows.Forms.Label();
LC064:              this.panel1 = new System.Windows.Forms.Panel();
LC065:              this.button1 = new System.Windows.Forms.Button();
LC066:              this.label2 = new System.Windows.Forms.Label();
LC067:              this.panel1.SuspendLayout();
LC068:              this.SuspendLayout();
                    //
                    // label1
                    //
LC069:              this.label1.AutoSize = true;
LC070:              this.label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 14F,
                      System.Drawing.FontStyle.Underline, System.Drawing.GraphicsUnit.Point,
                      ((byte)(0)));
LC071:              this.label1.Location = new System.Drawing.Point(190, 20);
LC072:              this.label1.Name = "label1";
LC073:              this.label1.Size = new System.Drawing.Size(120, 24);
LC074:              this.label1.TabIndex = 0;
LC075:              this.label1.Text = "Lost Controls";
                    //
                    // panel1
                    //
LC076:              this.panel1.BackColor = System.Drawing.SystemColors.Control;
LC077:              this.panel1.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
LC078:              this.panel1.Controls.Add(this.label2);
LC079:              this.panel1.Location = new System.Drawing.Point(100, 60);
LC080:              this.panel1.Name = "panel1";
LC081:              this.panel1.Size = new System.Drawing.Size(300, 150);
LC082:              this.panel1.TabIndex = 1;
                    //
                    // button1
                    //
LC083:              this.button1.Location = new System.Drawing.Point(16, 232);
LC084:              this.button1.Name = "button1";
LC085:              this.button1.Size = new System.Drawing.Size(75, 25);
LC086:              this.button1.TabIndex = 2;
LC087:              this.button1.Text = "Quit";
LC088:              this.button1.Click += new System.EventHandler(this.button1_Click);
                    //
                    // label2
                    //
LC089:              this.label2.AutoSize = true;
LC090:              this.label2.Font = new System.Drawing.Font("Microsoft Sans Serif", 10F,
                      System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point,
                      ((byte)(0)));
LC091:              this.label2.Location = new System.Drawing.Point(85, 96);
LC092:              this.label2.Name = "label2";
LC093:              this.label2.Size = new System.Drawing.Size(150, 17);
LC094:              this.label2.TabIndex = 0;
LC095:              this.label2.Text = "Label on Top of Panel 1";
                    //
                    // Form1
                    //
LC096:              this.AutoScaleDimensions = new System.Drawing.SizeF(5F, 13F);
LC097:              this.ClientSize = new System.Drawing.Size(484, 272);
LC098:              this.Controls.Add(this.button1);
LC099:              this.Controls.Add(this.panel1);
LC100:              this.Controls.Add(this.label1);
LC101:              this.Name = "Form1";
LC102:              this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
LC103:              this.Text = "Lost Controls";
LC104:              this.panel1.ResumeLayout(false);
LC105:              this.panel1.PerformLayout();
LC106:              this.ResumeLayout(false);
LC107:              this.PerformLayout();
LC108:            }
LC109:            #endregion

LC110:            private System.Windows.Forms.Label label1;
LC111:            private System.Windows.Forms.Panel panel1;
LC112:            private System.Windows.Forms.Button button1;
LC113:            private System.Windows.Forms.Label label2;
LC114:          }
LC115:        }


Appendix A: A Description of Keeping Track

Keeping Track (KT) is a suite of seven executables gathered under the umbrella of the KTEntryPoint program and located in your Projects\ KT Source Code directory. They were originally written to demonstrate the inner workings of the common C# statements found in the Visual C# compiler. The Entry Point window looks like this:

This suite was designed for those who invest in stocks, bonds, and mutual funds as part of their financial portfolio. The names of the stocks/bonds/funds are entered by the user in the program KTStartup, and a persistent file is created for each item to record the activity of the item on a weekly basis. The list of portfolio items is recorded in a file named Masterfile.dta, and KT uses this list to cycle through the items every time the weekly data is posted (in the same order that they are listed in Masterfile).

Whenever an item is purchased or sold, that purchase/sale is recorded in KTBuy_Sold (see the Buy or Sold Declarations button in Figure A-1).

Image from book
Figure A-1: Keeping Track Entry Point window

The user is encouraged to enter the KTWeeklyDataPosting program regularly (once a week is the optimum posting interval), and record the prevailing prices of the items in the stocks/bonds/funds portfolio (as reported on the Internet and in newspapers). The program works equally well whether the postings occur every week, once a month, or at random points during the year.

The purpose of this data posting is to prepare material for the next logical operation in the sequence: Calculate Net Worth. Calculate Net Worth is activated as soon as the user has completed the weekly data posting; it produces a hardcopy listing of the value of all portfolio items. The total at the bottom of the sheet is the user’s portfolio current net worth.

From time to time the user may elect to enter the Print or Plot Historical Data program and print out or display a plot of the collective data in his portfolio. The types of collective data available for prints/plots/displays is shown in Figure A-2.

Image from book
Figure A-2: Print/Plot Historical Files window

A typical KT plot is shown in Figure A-3. The X axis is the week of the year, and the Y axis is dollars. The vertical green line is the separator between 2002 and 2003.

Image from book
Figure A-3: KT Net Worth plot

There are also two file maintenance programs on the KT Entry Point window: KTRealphabetize and CopyToFloppy. KTRealphabetize realphabetizes Masterfile.dta, and CopyToFloppy copies all the data files to a floppy disk for safekeeping. The data files for one given portfolio item (such as IBM) are created with a calendar year, so the year 2005 data for IBM would be found in the IBM__2003.ddt file. This way, new data files can be created once a year to keep the data files from becoming too verbose (and last year’s data can be retained on one floppy disk).

Open table as spreadsheet

Item

KT Task

Program Executed

1.

Enter portfolio items names into Masterfile.

KTStartup

2.

Enter items purchased and total price of purchase.

KTBuy_Sold

3.

Enter items sold and total sale price.

KTBuy_Sold

4.

Post prices of all portfolio items, weekly or at random.

KTWeeklyDataPosting

4b.

Determine current net worth, based on latest data posting.

KTWeeklyDataPosting

5.

Print/plot/display historical, multiweek data:

  1. Print list of weekly net worth data since KT inception.

  2. Plot weekly net worth data (maximum of 52 weeks).

  3. Plot weekly net worth net gain/loss (max of 52 weeks).

  4. Print a *.ddt file (for example, IBM__2005.ddt) since KT inception.

  5. Display a *.ddt file, since KT inception.

  6. Plot a *.ddt file, since KT inception.

KTPlotter

6.

Realphabetize file Masterfile.dta.

KTRealphabetize

7.

Copy all data files to a floppy disk.

CopyToFloppy

The source code for these programs is contained in the App C --KTVC#TaggedCode.doc file. The listing is provided in Word so the programmer can search for any executable statements, etc., in the listing without having to search the listing visually.


Previous Section

Appendix B: C++ to C# Cross Reference

The table on the following pages compares the statement types available in C++ and C# and describes their use.

Statement Type | C++ Statement | C# Statement
Char Array Sizing |  | char[]charSS = newchar[6];
Char Array to String |  | strFilename = newstring(charFilename);
Close | close(); | No change
Close File | fclose(fd); | fs.Close();
Decrement Variable | ItemNo--; | No change
Digit Check |  | if(Char.IsDigit(textBox2.Text[1])) { } else { } ;
Directory Create |  | Directory.CreateDirectory("A:\\DDT");
Directory Exists? |  | if(Directory.Exists("A:\\DDT")
Draw Line |  | grfx.DrawLine(blackPen, 70+DX, 445, DX, 455);
Enable/Disable | Button1->Enabled = false; | button1.Enabled = false;
Encoding |  | StreamWriter x5 = newStreamWriter(Filename, true, Encoding.ASCII, 62)
Exit | exit(1); | No change
File Copy | FileCopy() or CopyFile() | See CF043-CF044 in the App C -- KTVC#TaggedCode.doc file.
File Append | if((fy = fopen("Filename", "at")) == NULL; | See 'StreamWriter', line SU0855 in the App C -- KTVC#TaggedCode.doc file.
File Length | Length = filelength(fd); | intLength = fs.Length;
File Read | fread(charWNW, sizeof(charMNDummy), 1, ff); | ww.ReadBlock(charDDTBlock, 0, 62);
File Write | fwrite(&charRecordStor[0], sizeof(Dummy), 1, fp); | xd.Write(strMNRecord);
Floatto String Format |  | strYW = ConvertToString(doubleJJ);
For | for(int pp = 0; pp < NORFORM1; pp++) | No change
GoTo | goto JUMP1; | No change
Graphics |  | Graphics grfx = ppea.Graphics;
If | if(FlagFlag == 0) { }; | No change
Include | #include | using System.xxx
Increment Variable | ItemNo++; | No change
Initiate Program | WinExec("KTPlot.exe", SW_SHOWNORMAL); | ProcessStartInfo sinfo = new PSI("KTPlot.exe"); Process.Start(sInfo);
Jump Label | Jump: | No change
Label Caption | Label2->Caption = AnsiString(charYW); | label2.Text = strYW;
List Alphabetize |  | See RE052-RE065 in the App C -- KTVC#TaggedCode.doc file.
Lower Case | tolower(strRecord); | textBox1.Text = (textBox1.Text).ToLower();
Message Box |  | MessageBox.Show("Cannot open file.");
MessageBox Response |  | DialogResult dr = MessageBox.Show(); if(dr == DialogResponse.Yes)
Open File | int fd; if((fd = open("Filename")) == –1; | FileStream fs; try, then catch.
Open File | FILE *fp; if((fp = fopen("Filename", "rt")) == NULL) | StreamReader w = new StreamReader("File");
OpenFile Dialog |  | See PL0282-PL0288 in the App C -- KTVC#TaggedCode.doc file.
Page Scale |  | grfx.PageScale = 0.5f;
Page Unit |  | grfx.PageUnit = GraphicsUnit.Document; // 1/300th of an inch.
Parsing |  | D1 = strBlock.Substring(5,5); D2 = strBlock.Substring(7,11);
Pen |  | Pen greenPen = new Pen(Color.Green,2);
Record Size | sizeof(Record); | Not used. Receiving location has variable size.
Rewind | rewind(ff); | Not used
Round |  | GainLoss = Decimal.Round(GainLoss,0); // Nothing after decimal point.
Simple Sort (Alphabetic) |  | See RE052-RE065 in the App C -- KTVC#TaggedCode.doc file.
String Array Sizing |  | Not used. Size placed in 'arrayname.Length'.
String Concatenation | strcat(charMNRecord, charShortYW); | strYW = strYW + textBox1.text;
String Copy | strcpy | strYW = textBox1.text;
String to Decimal |  | GainLoss = Decimal.Parse(strGainLoss);
String to Int |  | intYear = Convert.ToInt32(strShortYear);
String Length |  | Stringlength = textBox1.Text.Length;
Substring |  | string strShortYear = strTB1.Substring(2,2);
Upper Case | toupper(strRecord); | textBox1.Text = (textBox1.Text).ToUpper();
While | while(charNGLDummy[dd] != '.') { } | No change
Write Text to Printer | WriteLine | grfx.DrawString(strRecord, font1, Brushes.Black, 200, 600);
Previous Section


The page cannot be found
The page you are looking for might have been removed, had its name changed, or is temporarily unavailable.

Please try the following:

  • If you typed the page address in the Address bar, make sure that it is spelled correctly.
  • Open the home page, and then look for links to the information you want.
  • Click the Back button to try another link.

HTTP 404 - File not found
Internet Information Services

Technical Information (for support personnel)

  • More information:
    Microsoft Support